langfuse.client

   1import datetime as dt
   2import logging
   3import os
   4import re
   5import time
   6import tracemalloc
   7import typing
   8import urllib.parse
   9import uuid
  10import warnings
  11from contextlib import contextmanager
  12from dataclasses import dataclass
  13from enum import Enum
  14from typing import Any, Dict, List, Literal, Optional, Sequence, Union, overload
  15
  16import backoff
  17import httpx
  18
  19from langfuse.api.resources.commons.types.dataset_run_with_items import (
  20    DatasetRunWithItems,
  21)
  22from langfuse.api.resources.commons.types.observations_view import ObservationsView
  23from langfuse.api.resources.commons.types.session import Session
  24from langfuse.api.resources.commons.types.trace_with_details import TraceWithDetails
  25from langfuse.api.resources.datasets.types.paginated_dataset_runs import (
  26    PaginatedDatasetRuns,
  27)
  28from langfuse.api.resources.ingestion.types.create_event_body import CreateEventBody
  29from langfuse.api.resources.ingestion.types.create_generation_body import (
  30    CreateGenerationBody,
  31)
  32from langfuse.api.resources.ingestion.types.create_span_body import CreateSpanBody
  33from langfuse.api.resources.ingestion.types.score_body import ScoreBody
  34from langfuse.api.resources.ingestion.types.sdk_log_body import SdkLogBody
  35from langfuse.api.resources.ingestion.types.trace_body import TraceBody
  36from langfuse.api.resources.ingestion.types.update_generation_body import (
  37    UpdateGenerationBody,
  38)
  39from langfuse.api.resources.ingestion.types.update_span_body import UpdateSpanBody
  40from langfuse.api.resources.media import GetMediaResponse
  41from langfuse.api.resources.observations.types.observations_views import (
  42    ObservationsViews,
  43)
  44from langfuse.api.resources.prompts.types import (
  45    CreatePromptRequest_Chat,
  46    CreatePromptRequest_Text,
  47    Prompt_Chat,
  48    Prompt_Text,
  49)
  50from langfuse.api.resources.trace.types.traces import Traces
  51from langfuse.api.resources.utils.resources.pagination.types.meta_response import (
  52    MetaResponse,
  53)
  54from langfuse.model import (
  55    ChatMessageDict,
  56    ChatPromptClient,
  57    CreateDatasetItemRequest,
  58    CreateDatasetRequest,
  59    CreateDatasetRunItemRequest,
  60    DatasetItem,
  61    DatasetStatus,
  62    ModelUsage,
  63    PromptClient,
  64    TextPromptClient,
  65)
  66from langfuse.parse_error import handle_fern_exception
  67from langfuse.prompt_cache import PromptCache
  68
  69try:
  70    import pydantic.v1 as pydantic  # type: ignore
  71except ImportError:
  72    import pydantic  # type: ignore
  73
  74from langfuse._task_manager.task_manager import TaskManager
  75from langfuse.api.client import AsyncFernLangfuse, FernLangfuse
  76from langfuse.environment import get_common_release_envs
  77from langfuse.logging import clean_logger
  78from langfuse.media import LangfuseMedia
  79from langfuse.model import Dataset, MapValue, Observation, TraceWithFullDetails
  80from langfuse.request import LangfuseClient
  81from langfuse.types import MaskFunction, ScoreDataType, SpanLevel
  82from langfuse.utils import (
  83    _convert_usage_input,
  84    _create_prompt_context,
  85    _get_timestamp,
  86)
  87
  88from .version import __version__ as version
  89
  90ENVIRONMENT_PATTERN = r"^(?!langfuse)[a-z0-9-_]+$"
  91
  92
  93@dataclass
  94class FetchTracesResponse:
  95    """Response object for fetch_traces method."""
  96
  97    data: typing.List[TraceWithDetails]
  98    meta: MetaResponse
  99
 100
 101@dataclass
 102class FetchTraceResponse:
 103    """Response object for fetch_trace method."""
 104
 105    data: TraceWithFullDetails
 106
 107
 108@dataclass
 109class FetchObservationsResponse:
 110    """Response object for fetch_observations method."""
 111
 112    data: typing.List[ObservationsView]
 113    meta: MetaResponse
 114
 115
 116@dataclass
 117class FetchObservationResponse:
 118    """Response object for fetch_observation method."""
 119
 120    data: Observation
 121
 122
 123@dataclass
 124class FetchMediaResponse:
 125    """Response object for fetch_media method."""
 126
 127    data: GetMediaResponse
 128
 129
 130@dataclass
 131class FetchSessionsResponse:
 132    """Response object for fetch_sessions method."""
 133
 134    data: typing.List[Session]
 135    meta: MetaResponse
 136
 137
 138class Langfuse(object):
 139    """Langfuse Python client.
 140
 141    Attributes:
 142        log (logging.Logger): Logger for the Langfuse client.
 143        base_url (str): Base URL of the Langfuse API, serving as the root address for API endpoint construction.
 144        httpx_client (httpx.Client): HTTPX client utilized for executing requests to the Langfuse API.
 145        client (FernLangfuse): Core interface for Langfuse API interaction.
 146        task_manager (TaskManager): Task Manager dedicated to handling asynchronous tasks.
 147        release (str): Identifies the release number or hash of the application.
 148        prompt_cache (PromptCache): A cache for efficiently storing and retrieving PromptClient instances.
 149
 150    Example:
 151        Initiating the Langfuse client should always be first step to use Langfuse.
 152        ```python
 153        import os
 154        from langfuse import Langfuse
 155
 156        # Set the public and secret keys as environment variables
 157        os.environ['LANGFUSE_PUBLIC_KEY'] = public_key
 158        os.environ['LANGFUSE_SECRET_KEY'] = secret_key
 159
 160        # Initialize the Langfuse client using the credentials
 161        langfuse = Langfuse()
 162        ```
 163    """
 164
 165    log = logging.getLogger("langfuse")
 166    """Logger for the Langfuse client."""
 167
 168    host: str
 169    """Host of Langfuse API."""
 170
 171    project_id: Optional[str]
 172    """Project ID of the Langfuse project associated with the API keys provided."""
 173
 174    def __init__(
 175        self,
 176        public_key: Optional[str] = None,
 177        secret_key: Optional[str] = None,
 178        host: Optional[str] = None,
 179        release: Optional[str] = None,
 180        debug: bool = False,
 181        threads: Optional[int] = None,
 182        flush_at: Optional[int] = None,
 183        flush_interval: Optional[float] = None,
 184        max_retries: Optional[int] = None,
 185        timeout: Optional[int] = None,  # seconds
 186        sdk_integration: Optional[str] = "default",
 187        httpx_client: Optional[httpx.Client] = None,
 188        enabled: Optional[bool] = True,
 189        sample_rate: Optional[float] = None,
 190        mask: Optional[MaskFunction] = None,
 191        environment: Optional[str] = None,
 192    ):
 193        """Initialize the Langfuse client.
 194
 195        Args:
 196            public_key: Public API key of Langfuse project. Can be set via `LANGFUSE_PUBLIC_KEY` environment variable.
 197            secret_key: Secret API key of Langfuse project. Can be set via `LANGFUSE_SECRET_KEY` environment variable.
 198            host: Host of Langfuse API. Can be set via `LANGFUSE_HOST` environment variable. Defaults to `https://cloud.langfuse.com`.
 199            release: Release number/hash of the application to provide analytics grouped by release. Can be set via `LANGFUSE_RELEASE` environment variable.
 200            debug: Enables debug mode for more verbose logging. Can be set via `LANGFUSE_DEBUG` environment variable.
 201            threads: Number of consumer threads to execute network requests. Helps scaling the SDK for high load. Only increase this if you run into scaling issues.
 202            flush_at: Max batch size that's sent to the API.
 203            flush_interval: Max delay until a new batch is sent to the API.
 204            max_retries: Max number of retries in case of API/network errors.
 205            timeout: Timeout of API requests in seconds. Defaults to 20 seconds.
 206            httpx_client: Pass your own httpx client for more customizability of requests.
 207            sdk_integration: Used by intgerations that wrap the Langfuse SDK to add context for debugging and support. Not to be used directly.
 208            enabled: Enables or disables the Langfuse client. If disabled, all observability calls to the backend will be no-ops.
 209            sample_rate: Sampling rate for tracing. If set to 0.2, only 20% of the data will be sent to the backend. Can be set via `LANGFUSE_SAMPLE_RATE` environment variable.
 210            mask (langfuse.types.MaskFunction): Masking function for 'input' and 'output' fields in events. Function must take a single keyword argument `data` and return a serializable, masked version of the data.
 211            environment (optional): The tracing environment. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. Can bet set via `LANGFUSE_TRACING_ENVIRONMENT` environment variable.
 212
 213        Raises:
 214            ValueError: If public_key or secret_key are not set and not found in environment variables.
 215
 216        Example:
 217            Initiating the Langfuse client should always be first step to use Langfuse.
 218            ```python
 219            import os
 220            from langfuse import Langfuse
 221
 222            # Set the public and secret keys as environment variables
 223            os.environ['LANGFUSE_PUBLIC_KEY'] = public_key
 224            os.environ['LANGFUSE_SECRET_KEY'] = secret_key
 225
 226            # Initialize the Langfuse client using the credentials
 227            langfuse = Langfuse()
 228            ```
 229        """
 230        self.enabled = enabled
 231        public_key = public_key or os.environ.get("LANGFUSE_PUBLIC_KEY")
 232        secret_key = secret_key or os.environ.get("LANGFUSE_SECRET_KEY")
 233        sample_rate = (
 234            sample_rate
 235            if sample_rate
 236            is not None  # needs explicit None check, as 0 is a valid value
 237            else float(os.environ.get("LANGFUSE_SAMPLE_RATE", 1.0))
 238        )
 239
 240        if sample_rate is not None and (
 241            sample_rate > 1 or sample_rate < 0
 242        ):  # default value 1 will be set in the taskmanager
 243            self.enabled = False
 244            self.log.warning(
 245                "Langfuse client is disabled since the sample rate provided is not between 0 and 1."
 246            )
 247
 248        threads = threads or int(os.environ.get("LANGFUSE_THREADS", 1))
 249        flush_at = flush_at or int(os.environ.get("LANGFUSE_FLUSH_AT", 15))
 250        flush_interval = flush_interval or float(
 251            os.environ.get("LANGFUSE_FLUSH_INTERVAL", 0.5)
 252        )
 253
 254        max_retries = max_retries or int(os.environ.get("LANGFUSE_MAX_RETRIES", 3))
 255        timeout = timeout or int(os.environ.get("LANGFUSE_TIMEOUT", 20))
 256
 257        if not self.enabled:
 258            self.log.warning(
 259                "Langfuse client is disabled. No observability data will be sent."
 260            )
 261
 262        elif not public_key:
 263            self.enabled = False
 264            self.log.warning(
 265                "Langfuse client is disabled since no public_key was provided as a parameter or environment variable 'LANGFUSE_PUBLIC_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client"
 266            )
 267
 268        elif not secret_key:
 269            self.enabled = False
 270            self.log.warning(
 271                "Langfuse client is disabled since no secret_key was provided as a parameter or environment variable 'LANGFUSE_SECRET_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client"
 272            )
 273
 274        set_debug = debug if debug else (os.getenv("LANGFUSE_DEBUG", "False") == "True")
 275
 276        if set_debug is True:
 277            # Ensures that debug level messages are logged when debug mode is on.
 278            # Otherwise, defaults to WARNING level.
 279            # See https://docs.python.org/3/howto/logging.html#what-happens-if-no-configuration-is-provided
 280            logging.basicConfig()
 281            # Set level for all loggers under langfuse package
 282            logging.getLogger("langfuse").setLevel(logging.DEBUG)
 283
 284            clean_logger()
 285        else:
 286            logging.getLogger("langfuse").setLevel(logging.WARNING)
 287            clean_logger()
 288
 289        self.base_url = (
 290            host
 291            if host
 292            else os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com")
 293        )
 294
 295        self.environment = environment or os.environ.get("LANGFUSE_TRACING_ENVIRONMENT")
 296
 297        if self.environment and not bool(
 298            re.match(ENVIRONMENT_PATTERN, self.environment)
 299        ):
 300            self.log.error(
 301                f'Invalid environment specified "{environment}" that does not match validation pattern ("{ENVIRONMENT_PATTERN}"). Events will be rejected by Langfuse servers.'
 302            )
 303
 304        self.httpx_client = httpx_client or httpx.Client(timeout=timeout)
 305
 306        public_api_client = FernLangfuse(
 307            base_url=self.base_url,
 308            username=public_key,
 309            password=secret_key,
 310            x_langfuse_sdk_name="python",
 311            x_langfuse_sdk_version=version,
 312            x_langfuse_public_key=public_key,
 313            httpx_client=self.httpx_client,
 314            timeout=timeout,
 315        )
 316        async_public_api_client = AsyncFernLangfuse(
 317            base_url=self.base_url,
 318            username=public_key,
 319            password=secret_key,
 320            x_langfuse_sdk_name="python",
 321            x_langfuse_sdk_version=version,
 322            x_langfuse_public_key=public_key,
 323            timeout=timeout,
 324        )
 325
 326        self.api = public_api_client
 327        self.client = public_api_client  # legacy, to be removed in next major release
 328        self.async_api = async_public_api_client
 329
 330        langfuse_client = LangfuseClient(
 331            public_key=public_key,
 332            secret_key=secret_key,
 333            base_url=self.base_url,
 334            version=version,
 335            timeout=timeout,
 336            session=self.httpx_client,
 337        )
 338
 339        args = {
 340            "threads": threads,
 341            "flush_at": flush_at,
 342            "flush_interval": flush_interval,
 343            "max_retries": max_retries,
 344            "client": langfuse_client,
 345            "api_client": self.client,
 346            "public_key": public_key,
 347            "sdk_name": "python",
 348            "sdk_version": version,
 349            "sdk_integration": sdk_integration,
 350            "enabled": self.enabled,
 351            "sample_rate": sample_rate,
 352            "mask": mask,
 353        }
 354
 355        self.task_manager = TaskManager(**args)
 356
 357        self.trace_id = None
 358        self.project_id = None
 359
 360        self.release = self._get_release_value(release)
 361
 362        self.prompt_cache = PromptCache()
 363
 364    def _get_release_value(self, release: Optional[str] = None) -> Optional[str]:
 365        if release:
 366            return release
 367        elif "LANGFUSE_RELEASE" in os.environ:
 368            return os.environ["LANGFUSE_RELEASE"]
 369        else:
 370            return get_common_release_envs()
 371
 372    def _get_project_id(self) -> Optional[str]:
 373        """Fetch and return the current project id. Persisted across requests. Returns None if no project id is found for api keys."""
 374        if not self.project_id:
 375            proj = self.client.projects.get()
 376            if not proj.data or not proj.data[0].id:
 377                return None
 378
 379            self.project_id = proj.data[0].id
 380
 381        return self.project_id
 382
 383    def get_trace_id(self) -> str:
 384        """Get the current trace id."""
 385        return self.trace_id
 386
 387    def get_trace_url(self) -> str:
 388        """Get the URL of the current trace to view it in the Langfuse UI."""
 389        project_id = self._get_project_id()
 390        if not project_id:
 391            return f"{self.base_url}/trace/{self.trace_id}"
 392
 393        return f"{self.base_url}/project/{project_id}/traces/{self.trace_id}"
 394
 395    def get_dataset(
 396        self, name: str, *, fetch_items_page_size: Optional[int] = 50
 397    ) -> "DatasetClient":
 398        """Fetch a dataset by its name.
 399
 400        Args:
 401            name (str): The name of the dataset to fetch.
 402            fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
 403
 404        Returns:
 405            DatasetClient: The dataset with the given name.
 406        """
 407        try:
 408            self.log.debug(f"Getting datasets {name}")
 409            dataset = self.client.datasets.get(dataset_name=name)
 410
 411            dataset_items = []
 412            page = 1
 413            while True:
 414                new_items = self.client.dataset_items.list(
 415                    dataset_name=self._url_encode(name),
 416                    page=page,
 417                    limit=fetch_items_page_size,
 418                )
 419                dataset_items.extend(new_items.data)
 420                if new_items.meta.total_pages <= page:
 421                    break
 422                page += 1
 423
 424            items = [DatasetItemClient(i, langfuse=self) for i in dataset_items]
 425
 426            return DatasetClient(dataset, items=items)
 427        except Exception as e:
 428            handle_fern_exception(e)
 429            raise e
 430
 431    def get_dataset_item(self, id: str) -> "DatasetItemClient":
 432        """Get the dataset item with the given id."""
 433        try:
 434            self.log.debug(f"Getting dataset item {id}")
 435            dataset_item = self.client.dataset_items.get(id=id)
 436            return DatasetItemClient(dataset_item, langfuse=self)
 437        except Exception as e:
 438            handle_fern_exception(e)
 439            raise e
 440
 441    def auth_check(self) -> bool:
 442        """Check if the provided credentials (public and secret key) are valid.
 443
 444        Raises:
 445            Exception: If no projects were found for the provided credentials.
 446
 447        Note:
 448            This method is blocking. It is discouraged to use it in production code.
 449        """
 450        try:
 451            projects = self.client.projects.get()
 452            self.log.debug(
 453                f"Auth check successful, found {len(projects.data)} projects"
 454            )
 455            if len(projects.data) == 0:
 456                raise Exception(
 457                    "Auth check failed, no project found for the keys provided."
 458                )
 459            return True
 460
 461        except Exception as e:
 462            handle_fern_exception(e)
 463            raise e
 464
 465    def get_dataset_runs(
 466        self,
 467        dataset_name: str,
 468        *,
 469        page: typing.Optional[int] = None,
 470        limit: typing.Optional[int] = None,
 471    ) -> PaginatedDatasetRuns:
 472        """Get all dataset runs.
 473
 474        Args:
 475            dataset_name (str): Name of the dataset.
 476            page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None.
 477            limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50.
 478
 479        Returns:
 480            PaginatedDatasetRuns: The dataset runs.
 481        """
 482        try:
 483            self.log.debug("Getting dataset runs")
 484            return self.client.datasets.get_runs(
 485                dataset_name=self._url_encode(dataset_name), page=page, limit=limit
 486            )
 487        except Exception as e:
 488            handle_fern_exception(e)
 489            raise e
 490
 491    def get_dataset_run(
 492        self,
 493        dataset_name: str,
 494        dataset_run_name: str,
 495    ) -> DatasetRunWithItems:
 496        """Get a dataset run.
 497
 498        Args:
 499            dataset_name: Name of the dataset.
 500            dataset_run_name: Name of the dataset run.
 501
 502        Returns:
 503            DatasetRunWithItems: The dataset run.
 504        """
 505        try:
 506            self.log.debug(
 507                f"Getting dataset runs for dataset {dataset_name} and run {dataset_run_name}"
 508            )
 509            return self.client.datasets.get_run(
 510                dataset_name=self._url_encode(dataset_name),
 511                run_name=self._url_encode(dataset_run_name),
 512            )
 513        except Exception as e:
 514            handle_fern_exception(e)
 515            raise e
 516
 517    def create_dataset(
 518        self,
 519        name: str,
 520        description: Optional[str] = None,
 521        metadata: Optional[Any] = None,
 522    ) -> Dataset:
 523        """Create a dataset with the given name on Langfuse.
 524
 525        Args:
 526            name: Name of the dataset to create.
 527            description: Description of the dataset. Defaults to None.
 528            metadata: Additional metadata. Defaults to None.
 529
 530        Returns:
 531            Dataset: The created dataset as returned by the Langfuse API.
 532        """
 533        try:
 534            body = CreateDatasetRequest(
 535                name=name, description=description, metadata=metadata
 536            )
 537            self.log.debug(f"Creating datasets {body}")
 538            return self.client.datasets.create(request=body)
 539        except Exception as e:
 540            handle_fern_exception(e)
 541            raise e
 542
 543    def create_dataset_item(
 544        self,
 545        dataset_name: str,
 546        input: Optional[Any] = None,
 547        expected_output: Optional[Any] = None,
 548        metadata: Optional[Any] = None,
 549        source_trace_id: Optional[str] = None,
 550        source_observation_id: Optional[str] = None,
 551        status: Optional[DatasetStatus] = None,
 552        id: Optional[str] = None,
 553    ) -> DatasetItem:
 554        """Create a dataset item.
 555
 556        Upserts if an item with id already exists.
 557
 558        Args:
 559            dataset_name: Name of the dataset in which the dataset item should be created.
 560            input: Input data. Defaults to None. Can contain any dict, list or scalar.
 561            expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
 562            metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
 563            source_trace_id: Id of the source trace. Defaults to None.
 564            source_observation_id: Id of the source observation. Defaults to None.
 565            status: Status of the dataset item. Defaults to ACTIVE for newly created items.
 566            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.
 567
 568        Returns:
 569            DatasetItem: The created dataset item as returned by the Langfuse API.
 570
 571        Example:
 572            ```python
 573            from langfuse import Langfuse
 574
 575            langfuse = Langfuse()
 576
 577            # Uploading items to the Langfuse dataset named "capital_cities"
 578            langfuse.create_dataset_item(
 579                dataset_name="capital_cities",
 580                input={"input": {"country": "Italy"}},
 581                expected_output={"expected_output": "Rome"},
 582                metadata={"foo": "bar"}
 583            )
 584            ```
 585        """
 586        try:
 587            body = CreateDatasetItemRequest(
 588                datasetName=dataset_name,
 589                input=input,
 590                expectedOutput=expected_output,
 591                metadata=metadata,
 592                sourceTraceId=source_trace_id,
 593                sourceObservationId=source_observation_id,
 594                status=status,
 595                id=id,
 596            )
 597            self.log.debug(f"Creating dataset item {body}")
 598            return self.client.dataset_items.create(request=body)
 599        except Exception as e:
 600            handle_fern_exception(e)
 601            raise e
 602
 603    def fetch_trace(
 604        self,
 605        id: str,
 606    ) -> FetchTraceResponse:
 607        """Fetch a trace via the Langfuse API by its id.
 608
 609        Args:
 610            id: The id of the trace to fetch.
 611
 612        Returns:
 613            FetchTraceResponse: The trace with full details as returned by the Langfuse API on `data`.
 614
 615        Raises:
 616            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
 617        """
 618        try:
 619            self.log.debug(f"Getting trace {id}")
 620            trace = self.client.trace.get(id)
 621            return FetchTraceResponse(data=trace)
 622        except Exception as e:
 623            handle_fern_exception(e)
 624            raise e
 625
 626    def get_trace(
 627        self,
 628        id: str,
 629    ) -> TraceWithFullDetails:
 630        """Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead.
 631
 632        Args:
 633            id: The id of the trace to fetch.
 634
 635        Returns:
 636            TraceWithFullDetails: The trace with full details as returned by the Langfuse API.
 637
 638        Raises:
 639            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
 640        """
 641        warnings.warn(
 642            "get_trace is deprecated, use fetch_trace instead.",
 643            DeprecationWarning,
 644        )
 645
 646        try:
 647            self.log.debug(f"Getting trace {id}")
 648            return self.client.trace.get(id)
 649        except Exception as e:
 650            handle_fern_exception(e)
 651            raise e
 652
 653    def fetch_traces(
 654        self,
 655        *,
 656        page: Optional[int] = None,
 657        limit: Optional[int] = None,
 658        user_id: Optional[str] = None,
 659        name: Optional[str] = None,
 660        session_id: Optional[str] = None,
 661        from_timestamp: Optional[dt.datetime] = None,
 662        to_timestamp: Optional[dt.datetime] = None,
 663        order_by: Optional[str] = None,
 664        tags: Optional[Union[str, Sequence[str]]] = None,
 665    ) -> FetchTracesResponse:
 666        """Fetch a list of traces in the current project matching the given parameters.
 667
 668        Args:
 669            page (Optional[int]): Page number, starts at 1. Defaults to None.
 670            limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
 671            name (Optional[str]): Filter by name of traces. Defaults to None.
 672            user_id (Optional[str]): Filter by user_id. Defaults to None.
 673            session_id (Optional[str]): Filter by session_id. Defaults to None.
 674            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
 675            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
 676            order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None.
 677            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
 678
 679        Returns:
 680            FetchTracesResponse, list of traces on `data` and metadata on `meta`.
 681
 682        Raises:
 683            Exception: If an error occurred during the request.
 684        """
 685        try:
 686            self.log.debug(
 687                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
 688            )
 689            res = self.client.trace.list(
 690                page=page,
 691                limit=limit,
 692                name=name,
 693                user_id=user_id,
 694                session_id=session_id,
 695                from_timestamp=from_timestamp,
 696                to_timestamp=to_timestamp,
 697                order_by=order_by,
 698                tags=tags,
 699            )
 700            return FetchTracesResponse(data=res.data, meta=res.meta)
 701        except Exception as e:
 702            handle_fern_exception(e)
 703            raise e
 704
 705    def get_traces(
 706        self,
 707        *,
 708        page: Optional[int] = None,
 709        limit: Optional[int] = None,
 710        user_id: Optional[str] = None,
 711        name: Optional[str] = None,
 712        session_id: Optional[str] = None,
 713        from_timestamp: Optional[dt.datetime] = None,
 714        to_timestamp: Optional[dt.datetime] = None,
 715        order_by: Optional[str] = None,
 716        tags: Optional[Union[str, Sequence[str]]] = None,
 717    ) -> Traces:
 718        """Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead.
 719
 720        Args:
 721            page (Optional[int]): Page number, starts at 1. Defaults to None.
 722            limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
 723            name (Optional[str]): Filter by name of traces. Defaults to None.
 724            user_id (Optional[str]): Filter by user_id. Defaults to None.
 725            session_id (Optional[str]): Filter by session_id. Defaults to None.
 726            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
 727            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
 728            order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None.
 729            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
 730
 731        Returns:
 732            List of Traces
 733
 734        Raises:
 735            Exception: If an error occurred during the request.
 736        """
 737        warnings.warn(
 738            "get_traces is deprecated, use fetch_traces instead.",
 739            DeprecationWarning,
 740        )
 741        try:
 742            self.log.debug(
 743                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
 744            )
 745            return self.client.trace.list(
 746                page=page,
 747                limit=limit,
 748                name=name,
 749                user_id=user_id,
 750                session_id=session_id,
 751                from_timestamp=from_timestamp,
 752                to_timestamp=to_timestamp,
 753                order_by=order_by,
 754                tags=tags,
 755            )
 756        except Exception as e:
 757            handle_fern_exception(e)
 758            raise e
 759
 760    def fetch_observations(
 761        self,
 762        *,
 763        page: typing.Optional[int] = None,
 764        limit: typing.Optional[int] = None,
 765        name: typing.Optional[str] = None,
 766        user_id: typing.Optional[str] = None,
 767        trace_id: typing.Optional[str] = None,
 768        parent_observation_id: typing.Optional[str] = None,
 769        from_start_time: typing.Optional[dt.datetime] = None,
 770        to_start_time: typing.Optional[dt.datetime] = None,
 771        type: typing.Optional[str] = None,
 772    ) -> FetchObservationsResponse:
 773        """Get a list of observations in the current project matching the given parameters.
 774
 775        Args:
 776            page (Optional[int]): Page number of the observations to return. Defaults to None.
 777            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
 778            name (Optional[str]): Name of the observations to return. Defaults to None.
 779            user_id (Optional[str]): User identifier. Defaults to None.
 780            trace_id (Optional[str]): Trace identifier. Defaults to None.
 781            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
 782            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
 783            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
 784            type (Optional[str]): Type of the observation. Defaults to None.
 785
 786        Returns:
 787            FetchObservationsResponse, list of observations on `data` and metadata on `meta`.
 788
 789        Raises:
 790            Exception: If an error occurred during the request.
 791        """
 792        try:
 793            self.log.debug(
 794                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
 795            )
 796            res = self.client.observations.get_many(
 797                page=page,
 798                limit=limit,
 799                name=name,
 800                user_id=user_id,
 801                trace_id=trace_id,
 802                parent_observation_id=parent_observation_id,
 803                from_start_time=from_start_time,
 804                to_start_time=to_start_time,
 805                type=type,
 806            )
 807            return FetchObservationsResponse(data=res.data, meta=res.meta)
 808        except Exception as e:
 809            self.log.exception(e)
 810            raise e
 811
 812    def get_observations(
 813        self,
 814        *,
 815        page: typing.Optional[int] = None,
 816        limit: typing.Optional[int] = None,
 817        name: typing.Optional[str] = None,
 818        user_id: typing.Optional[str] = None,
 819        trace_id: typing.Optional[str] = None,
 820        parent_observation_id: typing.Optional[str] = None,
 821        from_start_time: typing.Optional[dt.datetime] = None,
 822        to_start_time: typing.Optional[dt.datetime] = None,
 823        type: typing.Optional[str] = None,
 824    ) -> ObservationsViews:
 825        """Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead.
 826
 827        Args:
 828            page (Optional[int]): Page number of the observations to return. Defaults to None.
 829            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
 830            name (Optional[str]): Name of the observations to return. Defaults to None.
 831            user_id (Optional[str]): User identifier. Defaults to None.
 832            trace_id (Optional[str]): Trace identifier. Defaults to None.
 833            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
 834            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
 835            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
 836            type (Optional[str]): Type of the observation. Defaults to None.
 837
 838        Returns:
 839            List of ObservationsViews: List of observations in the project matching the given parameters.
 840
 841        Raises:
 842            Exception: If an error occurred during the request.
 843        """
 844        warnings.warn(
 845            "get_observations is deprecated, use fetch_observations instead.",
 846            DeprecationWarning,
 847        )
 848        try:
 849            self.log.debug(
 850                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
 851            )
 852            return self.client.observations.get_many(
 853                page=page,
 854                limit=limit,
 855                name=name,
 856                user_id=user_id,
 857                trace_id=trace_id,
 858                parent_observation_id=parent_observation_id,
 859                from_start_time=from_start_time,
 860                to_start_time=to_start_time,
 861                type=type,
 862            )
 863        except Exception as e:
 864            handle_fern_exception(e)
 865            raise e
 866
 867    def get_generations(
 868        self,
 869        *,
 870        page: typing.Optional[int] = None,
 871        limit: typing.Optional[int] = None,
 872        name: typing.Optional[str] = None,
 873        user_id: typing.Optional[str] = None,
 874        trace_id: typing.Optional[str] = None,
 875        from_start_time: typing.Optional[dt.datetime] = None,
 876        to_start_time: typing.Optional[dt.datetime] = None,
 877        parent_observation_id: typing.Optional[str] = None,
 878    ) -> ObservationsViews:
 879        """Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead.
 880
 881        Args:
 882            page (Optional[int]): Page number of the generations to return. Defaults to None.
 883            limit (Optional[int]): Maximum number of generations to return. Defaults to None.
 884            name (Optional[str]): Name of the generations to return. Defaults to None.
 885            user_id (Optional[str]): User identifier of the generations to return. Defaults to None.
 886            trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None.
 887            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
 888            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
 889            parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None.
 890
 891        Returns:
 892            List of ObservationsViews: List of generations in the project matching the given parameters.
 893
 894        Raises:
 895            Exception: If an error occurred during the request.
 896        """
 897        warnings.warn(
 898            "get_generations is deprecated, use `fetch_observations(type='GENERATION')` instead.",
 899            DeprecationWarning,
 900        )
 901        return self.get_observations(
 902            page=page,
 903            limit=limit,
 904            name=name,
 905            user_id=user_id,
 906            trace_id=trace_id,
 907            parent_observation_id=parent_observation_id,
 908            from_start_time=from_start_time,
 909            to_start_time=to_start_time,
 910            type="GENERATION",
 911        )
 912
 913    def fetch_observation(
 914        self,
 915        id: str,
 916    ) -> FetchObservationResponse:
 917        """Get an observation in the current project with the given identifier.
 918
 919        Args:
 920            id: The identifier of the observation to fetch.
 921
 922        Returns:
 923            FetchObservationResponse: The observation with the given id on `data`.
 924
 925        Raises:
 926            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
 927        """
 928        try:
 929            self.log.debug(f"Getting observation {id}")
 930            observation = self.client.observations.get(id)
 931            return FetchObservationResponse(data=observation)
 932        except Exception as e:
 933            handle_fern_exception(e)
 934            raise e
 935
 936    def fetch_media(self, id: str) -> FetchMediaResponse:
 937        """Get media content by ID.
 938
 939        Args:
 940            id: The identifier of the media content to fetch.
 941
 942        Returns:
 943            FetchMediaResponse: The media data of the given id on `data`.
 944
 945        Raises:
 946            Exception: If the media content with the given id could not be found within the authenticated project or if an error occurred during the request.
 947        """
 948        try:
 949            return FetchMediaResponse(data=self.client.media.get(id))
 950        except Exception as e:
 951            handle_fern_exception(e)
 952            raise e
 953
 954    def resolve_media_references(
 955        self,
 956        *,
 957        obj: Any,
 958        resolve_with: Literal["base64_data_uri"],
 959        max_depth: int = 10,
 960        content_fetch_timeout_seconds: int = 10,
 961    ):
 962        """Replace media reference strings in an object with base64 data URIs.
 963
 964        This method recursively traverses an object (up to max_depth) looking for media reference strings
 965        in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using
 966        the provided Langfuse client and replaces the reference string with a base64 data URI.
 967
 968        If fetching media content fails for a reference string, a warning is logged and the reference
 969        string is left unchanged.
 970
 971        Args:
 972            obj: The object to process. Can be a primitive value, array, or nested object.
 973                If the object has a __dict__ attribute, a dict will be returned instead of the original object type.
 974            resolve_with: The representation of the media content to replace the media reference string with.
 975                Currently only "base64_data_uri" is supported.
 976            max_depth: int: The maximum depth to traverse the object. Default is 10.
 977            content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 10.
 978
 979        Returns:
 980            A deep copy of the input object with all media references replaced with base64 data URIs where possible.
 981            If the input object has a __dict__ attribute, a dict will be returned instead of the original object type.
 982
 983        Example:
 984            obj = {
 985                "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@",
 986                "nested": {
 987                    "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@"
 988                }
 989            }
 990
 991            result = await LangfuseMedia.resolve_media_references(obj, langfuse_client)
 992
 993            # Result:
 994            # {
 995            #     "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
 996            #     "nested": {
 997            #         "pdf": "data:application/pdf;base64,JVBERi0xLjcK..."
 998            #     }
 999            # }
1000        """
1001        return LangfuseMedia.resolve_media_references(
1002            langfuse_client=self,
1003            obj=obj,
1004            resolve_with=resolve_with,
1005            max_depth=max_depth,
1006            content_fetch_timeout_seconds=content_fetch_timeout_seconds,
1007        )
1008
1009    def get_observation(
1010        self,
1011        id: str,
1012    ) -> Observation:
1013        """Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead.
1014
1015        Args:
1016            id: The identifier of the observation to fetch.
1017
1018        Raises:
1019            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
1020        """
1021        warnings.warn(
1022            "get_observation is deprecated, use fetch_observation instead.",
1023            DeprecationWarning,
1024        )
1025        try:
1026            self.log.debug(f"Getting observation {id}")
1027            return self.client.observations.get(id)
1028        except Exception as e:
1029            handle_fern_exception(e)
1030            raise e
1031
1032    def fetch_sessions(
1033        self,
1034        *,
1035        page: typing.Optional[int] = None,
1036        limit: typing.Optional[int] = None,
1037        from_timestamp: typing.Optional[dt.datetime] = None,
1038        to_timestamp: typing.Optional[dt.datetime] = None,
1039    ) -> FetchSessionsResponse:
1040        """Get a list of sessions in the current project.
1041
1042        Args:
1043            page (Optional[int]): Page number of the sessions to return. Defaults to None.
1044            limit (Optional[int]): Maximum number of sessions to return. Defaults to None.
1045            from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None.
1046            to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None.
1047
1048        Returns:
1049            FetchSessionsResponse, list of sessions on `data` and metadata on `meta`.
1050
1051        Raises:
1052            Exception: If an error occurred during the request.
1053        """
1054        try:
1055            self.log.debug(
1056                f"Getting sessions... {page}, {limit}, {from_timestamp}, {to_timestamp}"
1057            )
1058            res = self.client.sessions.list(
1059                page=page,
1060                limit=limit,
1061                from_timestamp=from_timestamp,
1062                to_timestamp=to_timestamp,
1063            )
1064            return FetchSessionsResponse(data=res.data, meta=res.meta)
1065        except Exception as e:
1066            handle_fern_exception(e)
1067            raise e
1068
1069    @overload
1070    def get_prompt(
1071        self,
1072        name: str,
1073        version: Optional[int] = None,
1074        *,
1075        label: Optional[str] = None,
1076        type: Literal["chat"],
1077        cache_ttl_seconds: Optional[int] = None,
1078        fallback: Optional[List[ChatMessageDict]] = None,
1079        max_retries: Optional[int] = None,
1080        fetch_timeout_seconds: Optional[int] = None,
1081    ) -> ChatPromptClient: ...
1082
1083    @overload
1084    def get_prompt(
1085        self,
1086        name: str,
1087        version: Optional[int] = None,
1088        *,
1089        label: Optional[str] = None,
1090        type: Literal["text"] = "text",
1091        cache_ttl_seconds: Optional[int] = None,
1092        fallback: Optional[str] = None,
1093        max_retries: Optional[int] = None,
1094        fetch_timeout_seconds: Optional[int] = None,
1095    ) -> TextPromptClient: ...
1096
1097    def get_prompt(
1098        self,
1099        name: str,
1100        version: Optional[int] = None,
1101        *,
1102        label: Optional[str] = None,
1103        type: Literal["chat", "text"] = "text",
1104        cache_ttl_seconds: Optional[int] = None,
1105        fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None,
1106        max_retries: Optional[int] = None,
1107        fetch_timeout_seconds: Optional[int] = None,
1108    ) -> PromptClient:
1109        """Get a prompt.
1110
1111        This method attempts to fetch the requested prompt from the local cache. If the prompt is not found
1112        in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again
1113        and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will
1114        return the expired prompt as a fallback.
1115
1116        Args:
1117            name (str): The name of the prompt to retrieve.
1118
1119        Keyword Args:
1120            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.
1121            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.
1122            cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a
1123            keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0.
1124            type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text".
1125            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.
1126            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.
1127            fetch_timeout_seconds: Optional[int]: The timeout in milliseconds for fetching the prompt. Defaults to the default timeout set on the SDK, which is 10 seconds per default.
1128
1129        Returns:
1130            The prompt object retrieved from the cache or directly fetched if not cached or expired of type
1131            - TextPromptClient, if type argument is 'text'.
1132            - ChatPromptClient, if type argument is 'chat'.
1133
1134        Raises:
1135            Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
1136            expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
1137        """
1138        if version is not None and label is not None:
1139            raise ValueError("Cannot specify both version and label at the same time.")
1140
1141        if not name:
1142            raise ValueError("Prompt name cannot be empty.")
1143
1144        cache_key = PromptCache.generate_cache_key(name, version=version, label=label)
1145        bounded_max_retries = self._get_bounded_max_retries(
1146            max_retries, default_max_retries=2, max_retries_upper_bound=4
1147        )
1148
1149        self.log.debug(f"Getting prompt '{cache_key}'")
1150        cached_prompt = self.prompt_cache.get(cache_key)
1151
1152        if cached_prompt is None or cache_ttl_seconds == 0:
1153            self.log.debug(
1154                f"Prompt '{cache_key}' not found in cache or caching disabled."
1155            )
1156            try:
1157                return self._fetch_prompt_and_update_cache(
1158                    name,
1159                    version=version,
1160                    label=label,
1161                    ttl_seconds=cache_ttl_seconds,
1162                    max_retries=bounded_max_retries,
1163                    fetch_timeout_seconds=fetch_timeout_seconds,
1164                )
1165            except Exception as e:
1166                if fallback:
1167                    self.log.warning(
1168                        f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}"
1169                    )
1170
1171                    fallback_client_args = {
1172                        "name": name,
1173                        "prompt": fallback,
1174                        "type": type,
1175                        "version": version or 0,
1176                        "config": {},
1177                        "labels": [label] if label else [],
1178                        "tags": [],
1179                    }
1180
1181                    if type == "text":
1182                        return TextPromptClient(
1183                            prompt=Prompt_Text(**fallback_client_args),
1184                            is_fallback=True,
1185                        )
1186
1187                    if type == "chat":
1188                        return ChatPromptClient(
1189                            prompt=Prompt_Chat(**fallback_client_args),
1190                            is_fallback=True,
1191                        )
1192
1193                raise e
1194
1195        if cached_prompt.is_expired():
1196            self.log.debug(f"Stale prompt '{cache_key}' found in cache.")
1197            try:
1198                # refresh prompt in background thread, refresh_prompt deduplicates tasks
1199                self.log.debug(f"Refreshing prompt '{cache_key}' in background.")
1200                self.prompt_cache.add_refresh_prompt_task(
1201                    cache_key,
1202                    lambda: self._fetch_prompt_and_update_cache(
1203                        name,
1204                        version=version,
1205                        label=label,
1206                        ttl_seconds=cache_ttl_seconds,
1207                        max_retries=bounded_max_retries,
1208                        fetch_timeout_seconds=fetch_timeout_seconds,
1209                    ),
1210                )
1211                self.log.debug(f"Returning stale prompt '{cache_key}' from cache.")
1212                # return stale prompt
1213                return cached_prompt.value
1214
1215            except Exception as e:
1216                self.log.warning(
1217                    f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}"
1218                )
1219                # creation of refresh prompt task failed, return stale prompt
1220                return cached_prompt.value
1221
1222        return cached_prompt.value
1223
1224    def _fetch_prompt_and_update_cache(
1225        self,
1226        name: str,
1227        *,
1228        version: Optional[int] = None,
1229        label: Optional[str] = None,
1230        ttl_seconds: Optional[int] = None,
1231        max_retries: int,
1232        fetch_timeout_seconds,
1233    ) -> PromptClient:
1234        try:
1235            cache_key = PromptCache.generate_cache_key(
1236                name, version=version, label=label
1237            )
1238
1239            self.log.debug(f"Fetching prompt '{cache_key}' from server...")
1240
1241            @backoff.on_exception(
1242                backoff.constant, Exception, max_tries=max_retries, logger=None
1243            )
1244            def fetch_prompts():
1245                return self.client.prompts.get(
1246                    self._url_encode(name),
1247                    version=version,
1248                    label=label,
1249                    request_options={
1250                        "timeout_in_seconds": fetch_timeout_seconds,
1251                    }
1252                    if fetch_timeout_seconds is not None
1253                    else None,
1254                )
1255
1256            prompt_response = fetch_prompts()
1257
1258            if prompt_response.type == "chat":
1259                prompt = ChatPromptClient(prompt_response)
1260            else:
1261                prompt = TextPromptClient(prompt_response)
1262
1263            self.prompt_cache.set(cache_key, prompt, ttl_seconds)
1264
1265            return prompt
1266
1267        except Exception as e:
1268            self.log.error(f"Error while fetching prompt '{cache_key}': {str(e)}")
1269            raise e
1270
1271    def _get_bounded_max_retries(
1272        self,
1273        max_retries: Optional[int],
1274        *,
1275        default_max_retries: int = 2,
1276        max_retries_upper_bound: int = 4,
1277    ) -> int:
1278        if max_retries is None:
1279            return default_max_retries
1280
1281        bounded_max_retries = min(
1282            max(max_retries, 0),
1283            max_retries_upper_bound,
1284        )
1285
1286        return bounded_max_retries
1287
1288    @overload
1289    def create_prompt(
1290        self,
1291        *,
1292        name: str,
1293        prompt: List[ChatMessageDict],
1294        is_active: Optional[bool] = None,  # deprecated
1295        labels: List[str] = [],
1296        tags: Optional[List[str]] = None,
1297        type: Optional[Literal["chat"]],
1298        config: Optional[Any] = None,
1299        commit_message: Optional[str] = None,
1300    ) -> ChatPromptClient: ...
1301
1302    @overload
1303    def create_prompt(
1304        self,
1305        *,
1306        name: str,
1307        prompt: str,
1308        is_active: Optional[bool] = None,  # deprecated
1309        labels: List[str] = [],
1310        tags: Optional[List[str]] = None,
1311        type: Optional[Literal["text"]] = "text",
1312        config: Optional[Any] = None,
1313        commit_message: Optional[str] = None,
1314    ) -> TextPromptClient: ...
1315
1316    def create_prompt(
1317        self,
1318        *,
1319        name: str,
1320        prompt: Union[str, List[ChatMessageDict]],
1321        is_active: Optional[bool] = None,  # deprecated
1322        labels: List[str] = [],
1323        tags: Optional[List[str]] = None,
1324        type: Optional[Literal["chat", "text"]] = "text",
1325        config: Optional[Any] = None,
1326        commit_message: Optional[str] = None,
1327    ) -> PromptClient:
1328        """Create a new prompt in Langfuse.
1329
1330        Keyword Args:
1331            name : The name of the prompt to be created.
1332            prompt : The content of the prompt to be created.
1333            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.
1334            labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label.
1335            tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt.
1336            config: Additional structured data to be saved with the prompt. Defaults to None.
1337            type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text".
1338            commit_message: Optional string describing the change.
1339
1340        Returns:
1341            TextPromptClient: The prompt if type argument is 'text'.
1342            ChatPromptClient: The prompt if type argument is 'chat'.
1343        """
1344        try:
1345            self.log.debug(f"Creating prompt {name=}, {version=}, {labels=}")
1346
1347            # Handle deprecated is_active flag
1348            if is_active:
1349                self.log.warning(
1350                    "The 'is_active' flag is deprecated and will be removed in a future release. Please use the 'production' label instead."
1351                )
1352
1353                labels = labels if "production" in labels else labels + ["production"]
1354
1355            if type == "chat":
1356                if not isinstance(prompt, list):
1357                    raise ValueError(
1358                        "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes."
1359                    )
1360                request = CreatePromptRequest_Chat(
1361                    name=name,
1362                    prompt=prompt,
1363                    labels=labels,
1364                    tags=tags,
1365                    config=config or {},
1366                    commitMessage=commit_message,
1367                    type="chat",
1368                )
1369                server_prompt = self.client.prompts.create(request=request)
1370
1371                return ChatPromptClient(prompt=server_prompt)
1372
1373            if not isinstance(prompt, str):
1374                raise ValueError("For 'text' type, 'prompt' must be a string.")
1375
1376            request = CreatePromptRequest_Text(
1377                name=name,
1378                prompt=prompt,
1379                labels=labels,
1380                tags=tags,
1381                config=config or {},
1382                commitMessage=commit_message,
1383                type="text",
1384            )
1385
1386            server_prompt = self.client.prompts.create(request=request)
1387            return TextPromptClient(prompt=server_prompt)
1388
1389        except Exception as e:
1390            handle_fern_exception(e)
1391            raise e
1392
1393    def update_prompt(
1394        self,
1395        *,
1396        name: str,
1397        version: int,
1398        new_labels: List[str] = [],
1399    ):
1400        """Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.
1401
1402        Args:
1403            name (str): The name of the prompt to update.
1404            version (int): The version number of the prompt to update.
1405            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 [].
1406
1407        Returns:
1408            Prompt: The updated prompt from the Langfuse API.
1409
1410        """
1411        updated_prompt = self.client.prompt_version.update(
1412            name=name,
1413            version=version,
1414            new_labels=new_labels,
1415        )
1416        self.prompt_cache.invalidate(name)
1417        return updated_prompt
1418
1419    def _url_encode(self, url: str) -> str:
1420        return urllib.parse.quote(url)
1421
1422    def trace(
1423        self,
1424        *,
1425        id: typing.Optional[str] = None,
1426        name: typing.Optional[str] = None,
1427        user_id: typing.Optional[str] = None,
1428        session_id: typing.Optional[str] = None,
1429        version: typing.Optional[str] = None,
1430        input: typing.Optional[typing.Any] = None,
1431        output: typing.Optional[typing.Any] = None,
1432        metadata: typing.Optional[typing.Any] = None,
1433        tags: typing.Optional[typing.List[str]] = None,
1434        timestamp: typing.Optional[dt.datetime] = None,
1435        public: typing.Optional[bool] = None,
1436        **kwargs,
1437    ) -> "StatefulTraceClient":
1438        """Create a trace.
1439
1440        Args:
1441            id: The id of the trace can be set, defaults to a random id. Set it to link traces to external systems or when creating a distributed trace. Traces are upserted on id.
1442            name: Identifier of the trace. Useful for sorting/filtering in the UI.
1443            input: The input of the trace. Can be any JSON object.
1444            output: The output of the trace. Can be any JSON object.
1445            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
1446            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
1447            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
1448            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
1449            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
1450            tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
1451            timestamp: The timestamp of the trace. Defaults to the current time if not provided.
1452            public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project.
1453            **kwargs: Additional keyword arguments that can be included in the trace.
1454
1455        Returns:
1456            StatefulTraceClient: The created trace.
1457
1458        Example:
1459            ```python
1460            from langfuse import Langfuse
1461
1462            langfuse = Langfuse()
1463
1464            trace = langfuse.trace(
1465                name="example-application",
1466                user_id="user-1234")
1467            )
1468            ```
1469        """
1470        new_id = id or str(uuid.uuid4())
1471        self.trace_id = new_id
1472        try:
1473            new_dict = {
1474                "id": new_id,
1475                "name": name,
1476                "userId": user_id,
1477                "sessionId": session_id
1478                or kwargs.get("sessionId", None),  # backward compatibility
1479                "release": self.release,
1480                "version": version,
1481                "metadata": metadata,
1482                "input": input,
1483                "output": output,
1484                "tags": tags,
1485                "timestamp": timestamp or _get_timestamp(),
1486                "public": public,
1487                "environment": self.environment,
1488            }
1489            if kwargs is not None:
1490                new_dict.update(kwargs)
1491
1492            new_body = TraceBody(**new_dict)
1493
1494            self.log.debug(f"Creating trace {_filter_io_from_event_body(new_dict)}")
1495            event = {
1496                "id": str(uuid.uuid4()),
1497                "type": "trace-create",
1498                "body": new_body,
1499            }
1500
1501            self.task_manager.add_task(
1502                event,
1503            )
1504
1505        except Exception as e:
1506            self.log.exception(e)
1507        finally:
1508            self._log_memory_usage()
1509
1510            return StatefulTraceClient(
1511                self.client,
1512                new_id,
1513                StateType.TRACE,
1514                new_id,
1515                self.task_manager,
1516                self.environment,
1517            )
1518
1519    def _log_memory_usage(self):
1520        try:
1521            is_malloc_tracing_enabled = bool(int(os.getenv("PYTHONTRACEMALLOC", 0)))
1522            report_interval = int(os.getenv("LANGFUSE_DEBUG_MEMORY_REPORT_INTERVAL", 0))
1523            top_k_items = int(os.getenv("LANGFUSE_DEBUG_MEMORY_TOP_K", 10))
1524
1525            if (
1526                not is_malloc_tracing_enabled
1527                or report_interval <= 0
1528                or round(time.monotonic()) % report_interval != 0
1529            ):
1530                return
1531
1532            snapshot = tracemalloc.take_snapshot().statistics("lineno")
1533
1534            total_memory_usage = sum([stat.size for stat in snapshot]) / 1024 / 1024
1535            memory_usage_total_items = [f"{stat}" for stat in snapshot]
1536            memory_usage_langfuse_items = [
1537                stat for stat in memory_usage_total_items if "/langfuse/" in stat
1538            ]
1539
1540            logged_memory_usage = {
1541                "all_files": [f"{stat}" for stat in memory_usage_total_items][
1542                    :top_k_items
1543                ],
1544                "langfuse_files": [f"{stat}" for stat in memory_usage_langfuse_items][
1545                    :top_k_items
1546                ],
1547                "total_usage": f"{total_memory_usage:.2f} MB",
1548                "langfuse_queue_length": self.task_manager._ingestion_queue.qsize(),
1549            }
1550
1551            self.log.debug("Memory usage: ", logged_memory_usage)
1552
1553            event = SdkLogBody(log=logged_memory_usage)
1554            self.task_manager.add_task(
1555                {
1556                    "id": str(uuid.uuid4()),
1557                    "type": "sdk-log",
1558                    "timestamp": _get_timestamp(),
1559                    "body": event.dict(),
1560                }
1561            )
1562
1563        except Exception as e:
1564            self.log.exception(e)
1565
1566    @overload
1567    def score(
1568        self,
1569        *,
1570        name: str,
1571        value: float,
1572        data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None,
1573        trace_id: typing.Optional[str] = None,
1574        id: typing.Optional[str] = None,
1575        comment: typing.Optional[str] = None,
1576        observation_id: typing.Optional[str] = None,
1577        config_id: typing.Optional[str] = None,
1578        **kwargs,
1579    ) -> "StatefulClient": ...
1580
1581    @overload
1582    def score(
1583        self,
1584        *,
1585        name: str,
1586        value: str,
1587        data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL",
1588        trace_id: typing.Optional[str] = None,
1589        id: typing.Optional[str] = None,
1590        comment: typing.Optional[str] = None,
1591        observation_id: typing.Optional[str] = None,
1592        config_id: typing.Optional[str] = None,
1593        **kwargs,
1594    ) -> "StatefulClient": ...
1595
1596    def score(
1597        self,
1598        *,
1599        name: str,
1600        value: typing.Union[float, str],
1601        data_type: typing.Optional[ScoreDataType] = None,
1602        trace_id: typing.Optional[str] = None,
1603        id: typing.Optional[str] = None,
1604        comment: typing.Optional[str] = None,
1605        observation_id: typing.Optional[str] = None,
1606        config_id: typing.Optional[str] = None,
1607        **kwargs,
1608    ) -> "StatefulClient":
1609        """Create a score attached to a trace (and optionally an observation).
1610
1611        Args:
1612            name (str): Identifier of the score.
1613            value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
1614            data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present.
1615              When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
1616            trace_id (str): The id of the trace to which the score should be attached.
1617            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
1618            comment (Optional[str]): Additional context/explanation of the score.
1619            observation_id (Optional[str]): The id of the observation to which the score should be attached.
1620            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
1621            **kwargs: Additional keyword arguments to include in the score.
1622
1623        Returns:
1624            StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided).
1625
1626        Example:
1627            ```python
1628            from langfuse import Langfuse
1629
1630            langfuse = Langfuse()
1631
1632            # Create a trace
1633            trace = langfuse.trace(name="example-application")
1634
1635            # Get id of created trace
1636            trace_id = trace.id
1637
1638            # Add score to the trace
1639            trace = langfuse.score(
1640                trace_id=trace_id,
1641                name="user-explicit-feedback",
1642                value=0.9,
1643                comment="I like how personalized the response is"
1644            )
1645            ```
1646        """
1647        trace_id = trace_id or self.trace_id or str(uuid.uuid4())
1648        new_id = id or str(uuid.uuid4())
1649        try:
1650            new_dict = {
1651                "id": new_id,
1652                "trace_id": trace_id,
1653                "observation_id": observation_id,
1654                "name": name,
1655                "value": value,
1656                "data_type": data_type,
1657                "comment": comment,
1658                "config_id": config_id,
1659                "environment": self.environment,
1660                **kwargs,
1661            }
1662
1663            self.log.debug(f"Creating score {new_dict}...")
1664            new_body = ScoreBody(**new_dict)
1665
1666            event = {
1667                "id": str(uuid.uuid4()),
1668                "type": "score-create",
1669                "body": new_body,
1670            }
1671            self.task_manager.add_task(event)
1672
1673        except Exception as e:
1674            self.log.exception(e)
1675        finally:
1676            if observation_id is not None:
1677                return StatefulClient(
1678                    self.client,
1679                    observation_id,
1680                    StateType.OBSERVATION,
1681                    trace_id,
1682                    self.task_manager,
1683                    self.environment,
1684                )
1685            else:
1686                return StatefulClient(
1687                    self.client,
1688                    new_id,
1689                    StateType.TRACE,
1690                    new_id,
1691                    self.task_manager,
1692                    self.environment,
1693                )
1694
1695    def span(
1696        self,
1697        *,
1698        id: typing.Optional[str] = None,
1699        trace_id: typing.Optional[str] = None,
1700        parent_observation_id: typing.Optional[str] = None,
1701        name: typing.Optional[str] = None,
1702        start_time: typing.Optional[dt.datetime] = None,
1703        end_time: typing.Optional[dt.datetime] = None,
1704        metadata: typing.Optional[typing.Any] = None,
1705        level: typing.Optional[SpanLevel] = None,
1706        status_message: typing.Optional[str] = None,
1707        input: typing.Optional[typing.Any] = None,
1708        output: typing.Optional[typing.Any] = None,
1709        version: typing.Optional[str] = None,
1710        **kwargs,
1711    ) -> "StatefulSpanClient":
1712        """Create a span.
1713
1714        A span represents durations of units of work in a trace.
1715        Usually, you want to add a span nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
1716
1717        If no trace_id is provided, a new trace is created just for this span.
1718
1719        Args:
1720            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
1721            trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated.
1722            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1723            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
1724            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
1725            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
1726            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
1727            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
1728            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
1729            input (Optional[dict]): The input to the span. Can be any JSON object.
1730            output (Optional[dict]): The output to the span. Can be any JSON object.
1731            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1732            **kwargs: Additional keyword arguments to include in the span.
1733
1734        Returns:
1735            StatefulSpanClient: The created span.
1736
1737        Example:
1738            ```python
1739            from langfuse import Langfuse
1740
1741            langfuse = Langfuse()
1742
1743            trace = langfuse.trace(name = "llm-feature")
1744
1745            # Create a span
1746            retrieval = langfuse.span(name = "retrieval", trace_id = trace.id)
1747
1748            # Create a nested span
1749            nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id)
1750            ```
1751        """
1752        new_span_id = id or str(uuid.uuid4())
1753        new_trace_id = trace_id or str(uuid.uuid4())
1754        self.trace_id = new_trace_id
1755        try:
1756            span_body = {
1757                "id": new_span_id,
1758                "trace_id": new_trace_id,
1759                "name": name,
1760                "start_time": start_time or _get_timestamp(),
1761                "metadata": metadata,
1762                "input": input,
1763                "output": output,
1764                "level": level,
1765                "status_message": status_message,
1766                "parent_observation_id": parent_observation_id,
1767                "version": version,
1768                "end_time": end_time,
1769                "trace": {"release": self.release},
1770                "environment": self.environment,
1771                **kwargs,
1772            }
1773
1774            if trace_id is None:
1775                self._generate_trace(new_trace_id, name or new_trace_id)
1776
1777            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
1778
1779            span_body = CreateSpanBody(**span_body)
1780
1781            event = {
1782                "id": str(uuid.uuid4()),
1783                "type": "span-create",
1784                "body": span_body,
1785            }
1786
1787            self.task_manager.add_task(event)
1788
1789        except Exception as e:
1790            self.log.exception(e)
1791        finally:
1792            self._log_memory_usage()
1793
1794            return StatefulSpanClient(
1795                self.client,
1796                new_span_id,
1797                StateType.OBSERVATION,
1798                new_trace_id,
1799                self.task_manager,
1800                self.environment,
1801            )
1802
1803    def event(
1804        self,
1805        *,
1806        id: typing.Optional[str] = None,
1807        trace_id: typing.Optional[str] = None,
1808        parent_observation_id: typing.Optional[str] = None,
1809        name: typing.Optional[str] = None,
1810        start_time: typing.Optional[dt.datetime] = None,
1811        metadata: typing.Optional[typing.Any] = None,
1812        input: typing.Optional[typing.Any] = None,
1813        output: typing.Optional[typing.Any] = None,
1814        level: typing.Optional[SpanLevel] = None,
1815        status_message: typing.Optional[str] = None,
1816        version: typing.Optional[str] = None,
1817        **kwargs,
1818    ) -> "StatefulSpanClient":
1819        """Create an event.
1820
1821        An event represents a discrete event in a trace.
1822        Usually, you want to add a event nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
1823
1824        If no trace_id is provided, a new trace is created just for this event.
1825
1826        Args:
1827            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
1828            trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event.
1829            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1830            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
1831            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
1832            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
1833            input (Optional[Any]): The input to the event. Can be any JSON object.
1834            output (Optional[Any]): The output to the event. Can be any JSON object.
1835            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
1836            status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event.
1837            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
1838            **kwargs: Additional keyword arguments to include in the event.
1839
1840        Returns:
1841            StatefulSpanClient: The created event.
1842
1843        Example:
1844            ```python
1845            from langfuse import Langfuse
1846
1847            langfuse = Langfuse()
1848
1849            trace = langfuse.trace(name = "llm-feature")
1850
1851            # Create an event
1852            retrieval = langfuse.event(name = "retrieval", trace_id = trace.id)
1853            ```
1854        """
1855        event_id = id or str(uuid.uuid4())
1856        new_trace_id = trace_id or str(uuid.uuid4())
1857        self.trace_id = new_trace_id
1858        try:
1859            event_body = {
1860                "id": event_id,
1861                "trace_id": new_trace_id,
1862                "name": name,
1863                "start_time": start_time or _get_timestamp(),
1864                "metadata": metadata,
1865                "input": input,
1866                "output": output,
1867                "level": level,
1868                "status_message": status_message,
1869                "parent_observation_id": parent_observation_id,
1870                "version": version,
1871                "trace": {"release": self.release},
1872                "environment": self.environment,
1873                **kwargs,
1874            }
1875
1876            if trace_id is None:
1877                self._generate_trace(new_trace_id, name or new_trace_id)
1878
1879            request = CreateEventBody(**event_body)
1880
1881            event = {
1882                "id": str(uuid.uuid4()),
1883                "type": "event-create",
1884                "body": request,
1885            }
1886
1887            self.log.debug(
1888                f"Creating event {_filter_io_from_event_body(event_body)} ..."
1889            )
1890            self.task_manager.add_task(event)
1891
1892        except Exception as e:
1893            self.log.exception(e)
1894        finally:
1895            return StatefulSpanClient(
1896                self.client,
1897                event_id,
1898                StateType.OBSERVATION,
1899                new_trace_id,
1900                self.task_manager,
1901                self.environment,
1902            )
1903
1904    def generation(
1905        self,
1906        *,
1907        id: typing.Optional[str] = None,
1908        trace_id: typing.Optional[str] = None,
1909        parent_observation_id: typing.Optional[str] = None,
1910        name: typing.Optional[str] = None,
1911        start_time: typing.Optional[dt.datetime] = None,
1912        end_time: typing.Optional[dt.datetime] = None,
1913        completion_start_time: typing.Optional[dt.datetime] = None,
1914        metadata: typing.Optional[typing.Any] = None,
1915        level: typing.Optional[SpanLevel] = None,
1916        status_message: typing.Optional[str] = None,
1917        version: typing.Optional[str] = None,
1918        model: typing.Optional[str] = None,
1919        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
1920        input: typing.Optional[typing.Any] = None,
1921        output: typing.Optional[typing.Any] = None,
1922        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
1923        usage_details: typing.Optional[typing.Dict[str, int]] = None,
1924        cost_details: typing.Optional[typing.Dict[str, float]] = None,
1925        prompt: typing.Optional[PromptClient] = None,
1926        **kwargs,
1927    ) -> "StatefulGenerationClient":
1928        """Create a generation.
1929
1930        A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.
1931
1932        Usually, you want to add a generation nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
1933
1934        If no trace_id is provided, a new trace is created just for this generation.
1935
1936        Args:
1937            id (Optional[str]): The id of the generation can be set, defaults to random id.
1938            trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created
1939            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1940            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
1941            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
1942            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
1943            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
1944            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
1945            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
1946            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
1947            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1948            model (Optional[str]): The name of the model used for the generation.
1949            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
1950            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
1951            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
1952            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
1953            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
1954            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
1955            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
1956            **kwargs: Additional keyword arguments to include in the generation.
1957
1958        Returns:
1959            StatefulGenerationClient: The created generation.
1960
1961        Example:
1962            ```python
1963            from langfuse import Langfuse
1964
1965            langfuse = Langfuse()
1966
1967            # Create a generation in Langfuse
1968            generation = langfuse.generation(
1969                name="summary-generation",
1970                model="gpt-3.5-turbo",
1971                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
1972                input=[{"role": "system", "content": "You are a helpful assistant."},
1973                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
1974                metadata={"interface": "whatsapp"}
1975            )
1976            ```
1977        """
1978        new_trace_id = trace_id or str(uuid.uuid4())
1979        new_generation_id = id or str(uuid.uuid4())
1980        self.trace_id = new_trace_id
1981        try:
1982            generation_body = {
1983                "id": new_generation_id,
1984                "trace_id": new_trace_id,
1985                "release": self.release,
1986                "name": name,
1987                "start_time": start_time or _get_timestamp(),
1988                "metadata": metadata,
1989                "input": input,
1990                "output": output,
1991                "level": level,
1992                "status_message": status_message,
1993                "parent_observation_id": parent_observation_id,
1994                "version": version,
1995                "end_time": end_time,
1996                "completion_start_time": completion_start_time,
1997                "model": model,
1998                "model_parameters": model_parameters,
1999                "usage": _convert_usage_input(usage) if usage is not None else None,
2000                "usage_details": usage_details,
2001                "cost_details": cost_details,
2002                "trace": {"release": self.release},
2003                "environment": self.environment,
2004                **_create_prompt_context(prompt),
2005                **kwargs,
2006            }
2007
2008            if trace_id is None:
2009                trace = {
2010                    "id": new_trace_id,
2011                    "release": self.release,
2012                    "name": name,
2013                    "environment": self.environment,
2014                }
2015                request = TraceBody(**trace)
2016
2017                event = {
2018                    "id": str(uuid.uuid4()),
2019                    "type": "trace-create",
2020                    "body": request,
2021                }
2022
2023                self.log.debug("Creating trace...")
2024
2025                self.task_manager.add_task(event)
2026
2027            self.log.debug(
2028                f"Creating generation max {_filter_io_from_event_body(generation_body)}..."
2029            )
2030            request = CreateGenerationBody(**generation_body)
2031
2032            event = {
2033                "id": str(uuid.uuid4()),
2034                "type": "generation-create",
2035                "body": request,
2036            }
2037
2038            self.task_manager.add_task(event)
2039
2040        except Exception as e:
2041            self.log.exception(e)
2042        finally:
2043            return StatefulGenerationClient(
2044                self.client,
2045                new_generation_id,
2046                StateType.OBSERVATION,
2047                new_trace_id,
2048                self.task_manager,
2049                self.environment,
2050            )
2051
2052    def _generate_trace(self, trace_id: str, name: str):
2053        trace_dict = {
2054            "id": trace_id,
2055            "release": self.release,
2056            "name": name,
2057            "environment": self.environment,
2058        }
2059
2060        trace_body = TraceBody(**trace_dict)
2061
2062        event = {
2063            "id": str(uuid.uuid4()),
2064            "type": "trace-create",
2065            "body": trace_body,
2066        }
2067
2068        self.log.debug(f"Creating trace {_filter_io_from_event_body(trace_dict)}...")
2069        self.task_manager.add_task(event)
2070
2071    def join(self):
2072        """Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter.
2073
2074        If called before flushing, consumers might terminate before sending all events to Langfuse API. This method is called at exit of the SKD, right before the Python interpreter closes.
2075        To guarantee all messages have been delivered, you still need to call flush().
2076        """
2077        try:
2078            return self.task_manager.join()
2079        except Exception as e:
2080            self.log.exception(e)
2081
2082    def flush(self):
2083        """Flush the internal event queue to the Langfuse API. It blocks until the queue is empty. It should be called when the application shuts down.
2084
2085        Example:
2086            ```python
2087            from langfuse import Langfuse
2088
2089            langfuse = Langfuse()
2090
2091            # Some operations with Langfuse
2092
2093            # Flushing all events to end Langfuse cleanly
2094            langfuse.flush()
2095            ```
2096        """
2097        try:
2098            return self.task_manager.flush()
2099        except Exception as e:
2100            self.log.exception(e)
2101
2102    def shutdown(self):
2103        """Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated.
2104
2105        This function calls flush() and join() consecutively resulting in a complete shutdown of the SDK. On success of this function, no more events will be sent to Langfuse API.
2106        As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API.
2107        """
2108        try:
2109            self.prompt_cache._task_manager.shutdown()
2110
2111            # In logging.py, a handler is attached to the httpx logger.
2112            # To avoid a memory leak on singleton reset, remove all handlers
2113            httpx_logger = logging.getLogger("httpx")
2114            for handler in httpx_logger.handlers:
2115                httpx_logger.removeHandler(handler)
2116
2117            return self.task_manager.shutdown()
2118        except Exception as e:
2119            self.log.exception(e)
2120
2121
2122class StateType(Enum):
2123    """Enum to distinguish observation and trace states.
2124
2125    Attributes:
2126        OBSERVATION (int): Observation state.
2127        TRACE (int): Trace state.
2128    """
2129
2130    OBSERVATION = 1
2131    TRACE = 0
2132
2133
2134class StatefulClient(object):
2135    """Base class for handling stateful operations in the Langfuse system.
2136
2137    This client is capable of creating different nested Langfuse objects like spans, generations, scores, and events,
2138    associating them with either an observation or a trace based on the specified state type.
2139
2140    Attributes:
2141        client (FernLangfuse): Core interface for Langfuse API interactions.
2142        id (str): Unique identifier of the stateful client (either observation or trace).
2143        state_type (StateType): Enum indicating whether the client is an observation or a trace.
2144        trace_id (str): Id of the trace associated with the stateful client.
2145        task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2146        environment (Optional(str)): The tracing environment.
2147    """
2148
2149    log = logging.getLogger("langfuse")
2150
2151    def __init__(
2152        self,
2153        client: FernLangfuse,
2154        id: str,
2155        state_type: StateType,
2156        trace_id: str,
2157        task_manager: TaskManager,
2158        environment: Optional[str] = None,
2159    ):
2160        """Initialize the StatefulClient.
2161
2162        Args:
2163            client (FernLangfuse): Core interface for Langfuse API interactions.
2164            id (str): Unique identifier of the stateful client (either observation or trace).
2165            state_type (StateType): Enum indicating whether the client is an observation or a trace.
2166            trace_id (str): Id of the trace associated with the stateful client.
2167            task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2168        """
2169        self.client = client
2170        self.trace_id = trace_id
2171        self.id = id
2172        self.state_type = state_type
2173        self.task_manager = task_manager
2174
2175        self.environment = environment or os.environ.get("LANGFUSE_TRACING_ENVIRONMENT")
2176
2177        if self.environment and not bool(
2178            re.match(ENVIRONMENT_PATTERN, self.environment)
2179        ):
2180            self.log.warning(
2181                f'Invalid environment specified "{environment}" that does not match validation pattern ("{ENVIRONMENT_PATTERN}"). Setting will be ignored.'
2182            )
2183
2184    def _add_state_to_event(self, body: dict):
2185        if self.state_type == StateType.OBSERVATION:
2186            body["parent_observation_id"] = self.id
2187            body["trace_id"] = self.trace_id
2188        else:
2189            body["trace_id"] = self.id
2190        return body
2191
2192    def _add_default_values(self, body: dict):
2193        if body.get("start_time") is None:
2194            body["start_time"] = _get_timestamp()
2195        return body
2196
2197    def generation(
2198        self,
2199        *,
2200        id: typing.Optional[str] = None,
2201        name: typing.Optional[str] = None,
2202        start_time: typing.Optional[dt.datetime] = None,
2203        end_time: typing.Optional[dt.datetime] = None,
2204        metadata: typing.Optional[typing.Any] = None,
2205        level: typing.Optional[SpanLevel] = None,
2206        status_message: typing.Optional[str] = None,
2207        version: typing.Optional[str] = None,
2208        completion_start_time: typing.Optional[dt.datetime] = None,
2209        model: typing.Optional[str] = None,
2210        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2211        input: typing.Optional[typing.Any] = None,
2212        output: typing.Optional[typing.Any] = None,
2213        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2214        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2215        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2216        prompt: typing.Optional[PromptClient] = None,
2217        **kwargs,
2218    ) -> "StatefulGenerationClient":
2219        """Create a generation nested within the current observation or trace.
2220
2221        A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.
2222
2223        Args:
2224            id (Optional[str]): The id of the generation can be set, defaults to random id.
2225            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2226            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2227            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2228            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
2229            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2230            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2231            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
2232            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2233            model (Optional[str]): The name of the model used for the generation.
2234            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2235            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2236            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2237            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
2238            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
2239            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
2240            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2241            **kwargs: Additional keyword arguments to include in the generation.
2242
2243        Returns:
2244            StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.
2245
2246        Example:
2247            ```python
2248            from langfuse import Langfuse
2249
2250            langfuse = Langfuse()
2251
2252            # Create a trace
2253            trace = langfuse.trace(name = "llm-feature")
2254
2255            # Create a nested generation in Langfuse
2256            generation = trace.generation(
2257                name="summary-generation",
2258                model="gpt-3.5-turbo",
2259                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
2260                input=[{"role": "system", "content": "You are a helpful assistant."},
2261                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
2262                metadata={"interface": "whatsapp"}
2263            )
2264            ```
2265        """
2266        generation_id = id or str(uuid.uuid4())
2267        try:
2268            generation_body = {
2269                "id": generation_id,
2270                "name": name,
2271                "start_time": start_time or _get_timestamp(),
2272                "metadata": metadata,
2273                "level": level,
2274                "status_message": status_message,
2275                "version": version,
2276                "end_time": end_time,
2277                "completion_start_time": completion_start_time,
2278                "model": model,
2279                "model_parameters": model_parameters,
2280                "input": input,
2281                "output": output,
2282                "usage": _convert_usage_input(usage) if usage is not None else None,
2283                "usage_details": usage_details,
2284                "cost_details": cost_details,
2285                "environment": self.environment,
2286                **_create_prompt_context(prompt),
2287                **kwargs,
2288            }
2289
2290            generation_body = self._add_state_to_event(generation_body)
2291            new_body = self._add_default_values(generation_body)
2292
2293            new_body = CreateGenerationBody(**new_body)
2294
2295            event = {
2296                "id": str(uuid.uuid4()),
2297                "type": "generation-create",
2298                "body": new_body.dict(exclude_none=True, exclude_unset=False),
2299            }
2300
2301            self.log.debug(
2302                f"Creating generation {_filter_io_from_event_body(generation_body)}..."
2303            )
2304            self.task_manager.add_task(event)
2305
2306        except Exception as e:
2307            self.log.exception(e)
2308        finally:
2309            return StatefulGenerationClient(
2310                self.client,
2311                generation_id,
2312                StateType.OBSERVATION,
2313                self.trace_id,
2314                self.task_manager,
2315                self.environment,
2316            )
2317
2318    def span(
2319        self,
2320        *,
2321        id: typing.Optional[str] = None,
2322        name: typing.Optional[str] = None,
2323        start_time: typing.Optional[dt.datetime] = None,
2324        end_time: typing.Optional[dt.datetime] = None,
2325        metadata: typing.Optional[typing.Any] = None,
2326        input: typing.Optional[typing.Any] = None,
2327        output: typing.Optional[typing.Any] = None,
2328        level: typing.Optional[SpanLevel] = None,
2329        status_message: typing.Optional[str] = None,
2330        version: typing.Optional[str] = None,
2331        **kwargs,
2332    ) -> "StatefulSpanClient":
2333        """Create a span nested within the current observation or trace.
2334
2335        A span represents durations of units of work in a trace.
2336
2337        Args:
2338            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
2339            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2340            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2341            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2342            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2343            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2344            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
2345            input (Optional[dict]): The input to the span. Can be any JSON object.
2346            output (Optional[dict]): The output to the span. Can be any JSON object.
2347            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2348            **kwargs: Additional keyword arguments to include in the span.
2349
2350        Returns:
2351            StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.
2352
2353        Example:
2354            ```python
2355            from langfuse import Langfuse
2356
2357            langfuse = Langfuse()
2358
2359            # Create a trace
2360            trace = langfuse.trace(name = "llm-feature")
2361
2362            # Create a span
2363            retrieval = langfuse.span(name = "retrieval")
2364            ```
2365        """
2366        span_id = id or str(uuid.uuid4())
2367        try:
2368            span_body = {
2369                "id": span_id,
2370                "name": name,
2371                "start_time": start_time or _get_timestamp(),
2372                "metadata": metadata,
2373                "input": input,
2374                "output": output,
2375                "level": level,
2376                "status_message": status_message,
2377                "version": version,
2378                "end_time": end_time,
2379                "environment": self.environment,
2380                **kwargs,
2381            }
2382
2383            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
2384
2385            new_dict = self._add_state_to_event(span_body)
2386            new_body = self._add_default_values(new_dict)
2387
2388            event = CreateSpanBody(**new_body)
2389
2390            event = {
2391                "id": str(uuid.uuid4()),
2392                "type": "span-create",
2393                "body": event,
2394            }
2395
2396            self.task_manager.add_task(event)
2397        except Exception as e:
2398            self.log.exception(e)
2399        finally:
2400            return StatefulSpanClient(
2401                self.client,
2402                span_id,
2403                StateType.OBSERVATION,
2404                self.trace_id,
2405                self.task_manager,
2406                self.environment,
2407            )
2408
2409    @overload
2410    def score(
2411        self,
2412        *,
2413        id: typing.Optional[str] = None,
2414        name: str,
2415        value: float,
2416        data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None,
2417        comment: typing.Optional[str] = None,
2418        config_id: typing.Optional[str] = None,
2419        **kwargs,
2420    ) -> "StatefulClient": ...
2421
2422    @overload
2423    def score(
2424        self,
2425        *,
2426        id: typing.Optional[str] = None,
2427        name: str,
2428        value: str,
2429        data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL",
2430        comment: typing.Optional[str] = None,
2431        config_id: typing.Optional[str] = None,
2432        **kwargs,
2433    ) -> "StatefulClient": ...
2434
2435    def score(
2436        self,
2437        *,
2438        id: typing.Optional[str] = None,
2439        name: str,
2440        value: typing.Union[float, str],
2441        data_type: typing.Optional[ScoreDataType] = None,
2442        comment: typing.Optional[str] = None,
2443        config_id: typing.Optional[str] = None,
2444        **kwargs,
2445    ) -> "StatefulClient":
2446        """Create a score attached for the current observation or trace.
2447
2448        Args:
2449            name (str): Identifier of the score.
2450            value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
2451            data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present.
2452              When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
2453            comment (Optional[str]): Additional context/explanation of the score.
2454            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
2455            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
2456            **kwargs: Additional keyword arguments to include in the score.
2457
2458        Returns:
2459            StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.
2460
2461        Example:
2462            ```python
2463            from langfuse import Langfuse
2464
2465            langfuse = Langfuse()
2466
2467            # Create a trace
2468            trace = langfuse.trace(name="example-application")
2469
2470            # Add score to the trace
2471            trace = trace.score(
2472                name="user-explicit-feedback",
2473                value=0.8,
2474                comment="I like how personalized the response is"
2475            )
2476            ```
2477        """
2478        score_id = id or str(uuid.uuid4())
2479        try:
2480            new_score = {
2481                "id": score_id,
2482                "trace_id": self.trace_id,
2483                "name": name,
2484                "value": value,
2485                "data_type": data_type,
2486                "comment": comment,
2487                "config_id": config_id,
2488                "environment": self.environment,
2489                **kwargs,
2490            }
2491
2492            self.log.debug(f"Creating score {new_score}...")
2493
2494            new_dict = self._add_state_to_event(new_score)
2495
2496            if self.state_type == StateType.OBSERVATION:
2497                new_dict["observationId"] = self.id
2498
2499            request = ScoreBody(**new_dict)
2500
2501            event = {
2502                "id": str(uuid.uuid4()),
2503                "type": "score-create",
2504                "body": request,
2505            }
2506
2507            self.task_manager.add_task(event)
2508
2509        except Exception as e:
2510            self.log.exception(e)
2511        finally:
2512            return StatefulClient(
2513                self.client,
2514                self.id,
2515                self.state_type,
2516                self.trace_id,
2517                self.task_manager,
2518                self.environment,
2519            )
2520
2521    def event(
2522        self,
2523        *,
2524        id: typing.Optional[str] = None,
2525        name: typing.Optional[str] = None,
2526        start_time: typing.Optional[dt.datetime] = None,
2527        metadata: typing.Optional[typing.Any] = None,
2528        input: typing.Optional[typing.Any] = None,
2529        output: typing.Optional[typing.Any] = None,
2530        level: typing.Optional[SpanLevel] = None,
2531        status_message: typing.Optional[str] = None,
2532        version: typing.Optional[str] = None,
2533        **kwargs,
2534    ) -> "StatefulClient":
2535        """Create an event nested within the current observation or trace.
2536
2537        An event represents a discrete event in a trace.
2538
2539        Args:
2540            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
2541            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
2542            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
2543            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
2544            input (Optional[Any]): The input to the event. Can be any JSON object.
2545            output (Optional[Any]): The output to the event. Can be any JSON object.
2546            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2547            status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event.
2548            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
2549            **kwargs: Additional keyword arguments to include in the event.
2550
2551        Returns:
2552            StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations.
2553
2554        Example:
2555            ```python
2556            from langfuse import Langfuse
2557
2558            langfuse = Langfuse()
2559
2560            # Create a trace
2561            trace = langfuse.trace(name = "llm-feature")
2562
2563            # Create an event
2564            retrieval = trace.event(name = "retrieval")
2565            ```
2566        """
2567        event_id = id or str(uuid.uuid4())
2568        try:
2569            event_body = {
2570                "id": event_id,
2571                "name": name,
2572                "start_time": start_time or _get_timestamp(),
2573                "metadata": metadata,
2574                "input": input,
2575                "output": output,
2576                "level": level,
2577                "status_message": status_message,
2578                "version": version,
2579                "environment": self.environment,
2580                **kwargs,
2581            }
2582
2583            new_dict = self._add_state_to_event(event_body)
2584            new_body = self._add_default_values(new_dict)
2585
2586            request = CreateEventBody(**new_body)
2587
2588            event = {
2589                "id": str(uuid.uuid4()),
2590                "type": "event-create",
2591                "body": request,
2592            }
2593
2594            self.log.debug(
2595                f"Creating event {_filter_io_from_event_body(event_body)}..."
2596            )
2597            self.task_manager.add_task(event)
2598
2599        except Exception as e:
2600            self.log.exception(e)
2601        finally:
2602            return StatefulClient(
2603                self.client,
2604                event_id,
2605                StateType.OBSERVATION,
2606                self.trace_id,
2607                self.task_manager,
2608                self.environment,
2609            )
2610
2611    def get_trace_url(self):
2612        """Get the URL to see the current trace in the Langfuse UI."""
2613        return f"{self.client._client_wrapper._base_url}/trace/{self.trace_id}"
2614
2615
2616class StatefulGenerationClient(StatefulClient):
2617    """Class for handling stateful operations of generations in the Langfuse system. Inherits from StatefulClient.
2618
2619    This client extends the capabilities of the StatefulClient to specifically handle generation,
2620    allowing for the creation, update, and termination of generation processes in Langfuse.
2621
2622    Attributes:
2623        client (FernLangfuse): Core interface for Langfuse API interaction.
2624        id (str): Unique identifier of the generation.
2625        state_type (StateType): Type of the stateful entity (observation or trace).
2626        trace_id (str): Id of trace associated with the generation.
2627        task_manager (TaskManager): Manager for handling asynchronous tasks.
2628    """
2629
2630    log = logging.getLogger("langfuse")
2631
2632    def __init__(
2633        self,
2634        client: FernLangfuse,
2635        id: str,
2636        state_type: StateType,
2637        trace_id: str,
2638        task_manager: TaskManager,
2639        environment: Optional[str] = None,
2640    ):
2641        """Initialize the StatefulGenerationClient."""
2642        super().__init__(client, id, state_type, trace_id, task_manager, environment)
2643
2644    # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY
2645    def update(
2646        self,
2647        *,
2648        name: typing.Optional[str] = None,
2649        start_time: typing.Optional[dt.datetime] = None,
2650        end_time: typing.Optional[dt.datetime] = None,
2651        completion_start_time: typing.Optional[dt.datetime] = None,
2652        metadata: typing.Optional[typing.Any] = None,
2653        level: typing.Optional[SpanLevel] = None,
2654        status_message: typing.Optional[str] = None,
2655        version: typing.Optional[str] = None,
2656        model: typing.Optional[str] = None,
2657        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2658        input: typing.Optional[typing.Any] = None,
2659        output: typing.Optional[typing.Any] = None,
2660        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2661        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2662        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2663        prompt: typing.Optional[PromptClient] = None,
2664        **kwargs,
2665    ) -> "StatefulGenerationClient":
2666        """Update the generation.
2667
2668        Args:
2669            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2670            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2671            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2672            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
2673            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2674            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2675            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
2676            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2677            model (Optional[str]): The name of the model used for the generation.
2678            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2679            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2680            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2681            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
2682            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
2683            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
2684            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2685            **kwargs: Additional keyword arguments to include in the generation.
2686
2687        Returns:
2688            StatefulGenerationClient: The updated generation. Passthrough for chaining.
2689
2690        Example:
2691            ```python
2692            from langfuse import Langfuse
2693
2694            langfuse = Langfuse()
2695
2696            # Create a trace
2697            trace = langfuse.trace(name = "llm-feature")
2698
2699            # Create a nested generation in Langfuse
2700            generation = trace.generation(name="summary-generation")
2701
2702            # Update the generation
2703            generation = generation.update(metadata={"interface": "whatsapp"})
2704            ```
2705        """
2706        try:
2707            generation_body = {
2708                "id": self.id,
2709                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2710                "name": name,
2711                "start_time": start_time,
2712                "metadata": metadata,
2713                "level": level,
2714                "status_message": status_message,
2715                "version": version,
2716                "end_time": end_time,
2717                "completion_start_time": completion_start_time,
2718                "model": model,
2719                "model_parameters": model_parameters,
2720                "input": input,
2721                "output": output,
2722                "usage": _convert_usage_input(usage) if usage is not None else None,
2723                "usage_details": usage_details,
2724                "cost_details": cost_details,
2725                **_create_prompt_context(prompt),
2726                **kwargs,
2727            }
2728
2729            self.log.debug(
2730                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2731            )
2732
2733            request = UpdateGenerationBody(**generation_body)
2734
2735            event = {
2736                "id": str(uuid.uuid4()),
2737                "type": "generation-update",
2738                "body": request.dict(exclude_none=True, exclude_unset=False),
2739            }
2740
2741            self.log.debug(
2742                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2743            )
2744            self.task_manager.add_task(event)
2745
2746        except Exception as e:
2747            self.log.exception(e)
2748        finally:
2749            return StatefulGenerationClient(
2750                self.client,
2751                self.id,
2752                StateType.OBSERVATION,
2753                self.trace_id,
2754                self.task_manager,
2755                self.environment,
2756            )
2757
2758    def end(
2759        self,
2760        *,
2761        name: typing.Optional[str] = None,
2762        start_time: typing.Optional[dt.datetime] = None,
2763        end_time: typing.Optional[dt.datetime] = None,
2764        completion_start_time: typing.Optional[dt.datetime] = None,
2765        metadata: typing.Optional[typing.Any] = None,
2766        level: typing.Optional[SpanLevel] = None,
2767        status_message: typing.Optional[str] = None,
2768        version: typing.Optional[str] = None,
2769        model: typing.Optional[str] = None,
2770        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2771        input: typing.Optional[typing.Any] = None,
2772        output: typing.Optional[typing.Any] = None,
2773        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2774        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2775        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2776        prompt: typing.Optional[PromptClient] = None,
2777        **kwargs,
2778    ) -> "StatefulGenerationClient":
2779        """End the generation, optionally updating its properties.
2780
2781        Args:
2782            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2783            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2784            end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
2785            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
2786            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2787            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2788            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
2789            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2790            model (Optional[str]): The name of the model used for the generation.
2791            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2792            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2793            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2794            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
2795            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
2796            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
2797            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2798            **kwargs: Additional keyword arguments to include in the generation.
2799
2800        Returns:
2801            StatefulGenerationClient: The ended generation. Passthrough for chaining.
2802
2803        Example:
2804            ```python
2805            from langfuse import Langfuse
2806
2807            langfuse = Langfuse()
2808
2809            # Create a trace
2810            trace = langfuse.trace(name = "llm-feature")
2811
2812            # Create a nested generation in Langfuse
2813            generation = trace.generation(name="summary-generation")
2814
2815            # End the generation and update its properties
2816            generation = generation.end(metadata={"interface": "whatsapp"})
2817            ```
2818        """
2819        return self.update(
2820            name=name,
2821            start_time=start_time,
2822            end_time=end_time or _get_timestamp(),
2823            metadata=metadata,
2824            level=level,
2825            status_message=status_message,
2826            version=version,
2827            completion_start_time=completion_start_time,
2828            model=model,
2829            model_parameters=model_parameters,
2830            input=input,
2831            output=output,
2832            usage=usage,
2833            usage_details=usage_details,
2834            cost_details=cost_details,
2835            prompt=prompt,
2836            **kwargs,
2837        )
2838
2839
2840class StatefulSpanClient(StatefulClient):
2841    """Class for handling stateful operations of spans in the Langfuse system. Inherits from StatefulClient.
2842
2843    Attributes:
2844        client (FernLangfuse): Core interface for Langfuse API interaction.
2845        id (str): Unique identifier of the span.
2846        state_type (StateType): Type of the stateful entity (observation or trace).
2847        trace_id (str): Id of trace associated with the span.
2848        task_manager (TaskManager): Manager for handling asynchronous tasks.
2849    """
2850
2851    log = logging.getLogger("langfuse")
2852
2853    def __init__(
2854        self,
2855        client: FernLangfuse,
2856        id: str,
2857        state_type: StateType,
2858        trace_id: str,
2859        task_manager: TaskManager,
2860        environment: Optional[str] = None,
2861    ):
2862        """Initialize the StatefulSpanClient."""
2863        super().__init__(client, id, state_type, trace_id, task_manager, environment)
2864
2865    # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY
2866    def update(
2867        self,
2868        *,
2869        name: typing.Optional[str] = None,
2870        start_time: typing.Optional[dt.datetime] = None,
2871        end_time: typing.Optional[dt.datetime] = None,
2872        metadata: typing.Optional[typing.Any] = None,
2873        input: typing.Optional[typing.Any] = None,
2874        output: typing.Optional[typing.Any] = None,
2875        level: typing.Optional[SpanLevel] = None,
2876        status_message: typing.Optional[str] = None,
2877        version: typing.Optional[str] = None,
2878        **kwargs,
2879    ) -> "StatefulSpanClient":
2880        """Update the span.
2881
2882        Args:
2883            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2884            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2885            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2886            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2887            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2888            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
2889            input (Optional[dict]): The input to the span. Can be any JSON object.
2890            output (Optional[dict]): The output to the span. Can be any JSON object.
2891            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2892            **kwargs: Additional keyword arguments to include in the span.
2893
2894        Returns:
2895            StatefulSpanClient: The updated span. Passthrough for chaining.
2896
2897        Example:
2898            ```python
2899            from langfuse import Langfuse
2900
2901            langfuse = Langfuse()
2902
2903            # Create a trace
2904            trace = langfuse.trace(name = "llm-feature")
2905
2906            # Create a nested span in Langfuse
2907            span = trace.span(name="retrieval")
2908
2909            # Update the span
2910            span = span.update(metadata={"interface": "whatsapp"})
2911            ```
2912        """
2913        try:
2914            span_body = {
2915                "id": self.id,
2916                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2917                "name": name,
2918                "start_time": start_time,
2919                "metadata": metadata,
2920                "input": input,
2921                "output": output,
2922                "level": level,
2923                "status_message": status_message,
2924                "version": version,
2925                "end_time": end_time,
2926                **kwargs,
2927            }
2928            self.log.debug(f"Update span {_filter_io_from_event_body(span_body)}...")
2929
2930            request = UpdateSpanBody(**span_body)
2931
2932            event = {
2933                "id": str(uuid.uuid4()),
2934                "type": "span-update",
2935                "body": request,
2936            }
2937
2938            self.task_manager.add_task(event)
2939        except Exception as e:
2940            self.log.exception(e)
2941        finally:
2942            return StatefulSpanClient(
2943                self.client,
2944                self.id,
2945                StateType.OBSERVATION,
2946                self.trace_id,
2947                self.task_manager,
2948                self.environment,
2949            )
2950
2951    def end(
2952        self,
2953        *,
2954        name: typing.Optional[str] = None,
2955        start_time: typing.Optional[dt.datetime] = None,
2956        end_time: typing.Optional[dt.datetime] = None,
2957        metadata: typing.Optional[typing.Any] = None,
2958        input: typing.Optional[typing.Any] = None,
2959        output: typing.Optional[typing.Any] = None,
2960        level: typing.Optional[SpanLevel] = None,
2961        status_message: typing.Optional[str] = None,
2962        version: typing.Optional[str] = None,
2963        **kwargs,
2964    ) -> "StatefulSpanClient":
2965        """End the span, optionally updating its properties.
2966
2967        Args:
2968            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2969            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2970            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2971            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2972            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2973            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
2974            input (Optional[dict]): The input to the span. Can be any JSON object.
2975            output (Optional[dict]): The output to the span. Can be any JSON object.
2976            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2977            **kwargs: Additional keyword arguments to include in the span.
2978
2979        Returns:
2980            StatefulSpanClient: The updated span. Passthrough for chaining.
2981
2982        Example:
2983            ```python
2984            from langfuse import Langfuse
2985
2986            langfuse = Langfuse()
2987
2988            # Create a trace
2989            trace = langfuse.trace(name = "llm-feature")
2990
2991            # Create a nested span in Langfuse
2992            span = trace.span(name="retrieval")
2993
2994            # End the span and update its properties
2995            span = span.end(metadata={"interface": "whatsapp"})
2996            ```
2997        """
2998        try:
2999            span_body = {
3000                "name": name,
3001                "start_time": start_time,
3002                "metadata": metadata,
3003                "input": input,
3004                "output": output,
3005                "level": level,
3006                "status_message": status_message,
3007                "version": version,
3008                "end_time": end_time or _get_timestamp(),
3009                **kwargs,
3010            }
3011            return self.update(**span_body)
3012
3013        except Exception as e:
3014            self.log.warning(e)
3015        finally:
3016            return StatefulSpanClient(
3017                self.client,
3018                self.id,
3019                StateType.OBSERVATION,
3020                self.trace_id,
3021                self.task_manager,
3022                self.environment,
3023            )
3024
3025    def get_langchain_handler(self, update_parent: bool = False):
3026        """Get langchain callback handler associated with the current span.
3027
3028        Args:
3029            update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
3030
3031        Returns:
3032            CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
3033        """
3034        from langfuse.callback import CallbackHandler
3035
3036        return CallbackHandler(
3037            stateful_client=self, update_stateful_client=update_parent
3038        )
3039
3040
3041class StatefulTraceClient(StatefulClient):
3042    """Class for handling stateful operations of traces in the Langfuse system. Inherits from StatefulClient.
3043
3044    Attributes:
3045        client (FernLangfuse): Core interface for Langfuse API interaction.
3046        id (str): Unique identifier of the trace.
3047        state_type (StateType): Type of the stateful entity (observation or trace).
3048        trace_id (str): The trace ID associated with this client.
3049        task_manager (TaskManager): Manager for handling asynchronous tasks.
3050    """
3051
3052    log = logging.getLogger("langfuse")
3053
3054    def __init__(
3055        self,
3056        client: FernLangfuse,
3057        id: str,
3058        state_type: StateType,
3059        trace_id: str,
3060        task_manager: TaskManager,
3061        environment: Optional[str] = None,
3062    ):
3063        """Initialize the StatefulTraceClient."""
3064        super().__init__(client, id, state_type, trace_id, task_manager, environment)
3065        self.task_manager = task_manager
3066
3067    def update(
3068        self,
3069        *,
3070        name: typing.Optional[str] = None,
3071        user_id: typing.Optional[str] = None,
3072        session_id: typing.Optional[str] = None,
3073        version: typing.Optional[str] = None,
3074        release: typing.Optional[str] = None,
3075        input: typing.Optional[typing.Any] = None,
3076        output: typing.Optional[typing.Any] = None,
3077        metadata: typing.Optional[typing.Any] = None,
3078        tags: typing.Optional[typing.List[str]] = None,
3079        public: typing.Optional[bool] = None,
3080        **kwargs,
3081    ) -> "StatefulTraceClient":
3082        """Update the trace.
3083
3084        Args:
3085            name: Identifier of the trace. Useful for sorting/filtering in the UI.
3086            input: The input of the trace. Can be any JSON object.
3087            output: The output of the trace. Can be any JSON object.
3088            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
3089            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
3090            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
3091            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
3092            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
3093            tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
3094            public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project.
3095            **kwargs: Additional keyword arguments that can be included in the trace.
3096
3097        Returns:
3098            StatefulTraceClient: The updated trace. Passthrough for chaining.
3099
3100        Example:
3101            ```python
3102            from langfuse import Langfuse
3103
3104            langfuse = Langfuse()
3105
3106            # Create a trace
3107            trace = langfuse.trace(
3108                name="example-application",
3109                user_id="user-1234")
3110            )
3111
3112            # Update the trace
3113            trace = trace.update(
3114                output={"result": "success"},
3115                metadata={"interface": "whatsapp"}
3116            )
3117            ```
3118        """
3119        try:
3120            trace_body = {
3121                "id": self.id,
3122                "name": name,
3123                "userId": user_id,
3124                "sessionId": session_id
3125                or kwargs.get("sessionId", None),  # backward compatibility
3126                "version": version,
3127                "release": release,
3128                "input": input,
3129                "output": output,
3130                "metadata": metadata,
3131                "public": public,
3132                "tags": tags,
3133                **kwargs,
3134            }
3135            self.log.debug(f"Update trace {_filter_io_from_event_body(trace_body)}...")
3136
3137            request = TraceBody(**trace_body)
3138
3139            event = {
3140                "id": str(uuid.uuid4()),
3141                "type": "trace-create",
3142                "body": request,
3143            }
3144
3145            self.task_manager.add_task(event)
3146
3147        except Exception as e:
3148            self.log.exception(e)
3149        finally:
3150            return StatefulTraceClient(
3151                self.client,
3152                self.id,
3153                StateType.TRACE,
3154                self.trace_id,
3155                self.task_manager,
3156                self.environment,
3157            )
3158
3159    def get_langchain_handler(self, update_parent: bool = False):
3160        """Get langchain callback handler associated with the current trace.
3161
3162        This method creates and returns a CallbackHandler instance, linking it with the current
3163        trace. Use this if you want to group multiple Langchain runs within a single trace.
3164
3165        Args:
3166            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
3167
3168        Raises:
3169            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
3170
3171        Returns:
3172            CallbackHandler: Langchain callback handler linked to the current trace.
3173
3174        Example:
3175            ```python
3176            from langfuse import Langfuse
3177
3178            langfuse = Langfuse()
3179
3180            # Create a trace
3181            trace = langfuse.trace(name = "llm-feature")
3182
3183            # Get a langchain callback handler
3184            handler = trace.get_langchain_handler()
3185            ```
3186        """
3187        try:
3188            from langfuse.callback import CallbackHandler
3189
3190            self.log.debug(f"Creating new handler for trace {self.id}")
3191
3192            return CallbackHandler(
3193                stateful_client=self,
3194                debug=self.log.level == logging.DEBUG,
3195                update_stateful_client=update_parent,
3196            )
3197        except Exception as e:
3198            self.log.exception(e)
3199
3200    def getNewHandler(self):
3201        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
3202        return self.get_langchain_handler()
3203
3204
3205class DatasetItemClient:
3206    """Class for managing dataset items in Langfuse.
3207
3208    Args:
3209        id (str): Unique identifier of the dataset item.
3210        status (DatasetStatus): The status of the dataset item. Can be either 'ACTIVE' or 'ARCHIVED'.
3211        input (Any): Input data of the dataset item.
3212        expected_output (Optional[Any]): Expected output of the dataset item.
3213        metadata (Optional[Any]): Additional metadata of the dataset item.
3214        source_trace_id (Optional[str]): Identifier of the source trace.
3215        source_observation_id (Optional[str]): Identifier of the source observation.
3216        dataset_id (str): Identifier of the dataset to which this item belongs.
3217        dataset_name (str): Name of the dataset to which this item belongs.
3218        created_at (datetime): Timestamp of dataset item creation.
3219        updated_at (datetime): Timestamp of the last update to the dataset item.
3220        langfuse (Langfuse): Instance of Langfuse client for API interactions.
3221
3222    Example:
3223        ```python
3224        from langfuse import Langfuse
3225
3226        langfuse = Langfuse()
3227
3228        dataset = langfuse.get_dataset("<dataset_name>")
3229
3230        for item in dataset.items:
3231            # Generate a completion using the input of every item
3232            completion, generation = llm_app.run(item.input)
3233
3234            # Evaluate the completion
3235            generation.score(
3236                name="example-score",
3237                value=1
3238            )
3239        ```
3240    """
3241
3242    log = logging.getLogger("langfuse")
3243
3244    id: str
3245    status: DatasetStatus
3246    input: typing.Any
3247    expected_output: typing.Optional[typing.Any]
3248    metadata: Optional[Any]
3249    source_trace_id: typing.Optional[str]
3250    source_observation_id: typing.Optional[str]
3251    dataset_id: str
3252    dataset_name: str
3253    created_at: dt.datetime
3254    updated_at: dt.datetime
3255
3256    langfuse: Langfuse
3257
3258    def __init__(self, dataset_item: DatasetItem, langfuse: Langfuse):
3259        """Initialize the DatasetItemClient."""
3260        self.id = dataset_item.id
3261        self.status = dataset_item.status
3262        self.input = dataset_item.input
3263        self.expected_output = dataset_item.expected_output
3264        self.metadata = dataset_item.metadata
3265        self.source_trace_id = dataset_item.source_trace_id
3266        self.source_observation_id = dataset_item.source_observation_id
3267        self.dataset_id = dataset_item.dataset_id
3268        self.dataset_name = dataset_item.dataset_name
3269        self.created_at = dataset_item.created_at
3270        self.updated_at = dataset_item.updated_at
3271
3272        self.langfuse = langfuse
3273
3274    def flush(self, observation: StatefulClient, run_name: str):
3275        """Flushes an observations task manager's queue.
3276
3277        Used before creating a dataset run item to ensure all events are persistent.
3278
3279        Args:
3280            observation (StatefulClient): The observation or trace client associated with the dataset item.
3281            run_name (str): The name of the dataset run.
3282        """
3283        observation.task_manager.flush()
3284
3285    def link(
3286        self,
3287        trace_or_observation: typing.Union[StatefulClient, str, None],
3288        run_name: str,
3289        run_metadata: Optional[Any] = None,
3290        run_description: Optional[str] = None,
3291        trace_id: Optional[str] = None,
3292        observation_id: Optional[str] = None,
3293    ):
3294        """Link the dataset item to observation within a specific dataset run. Creates a dataset run item.
3295
3296        Args:
3297            trace_or_observation (Union[StatefulClient, str, None]): The trace or observation object to link. Deprecated: can also be an observation ID.
3298            run_name (str): The name of the dataset run.
3299            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3300            run_description (Optional[str]): Description of the dataset run.
3301            trace_id (Optional[str]): The trace ID to link to the dataset item. Set trace_or_observation to None if trace_id is provided.
3302            observation_id (Optional[str]): The observation ID to link to the dataset item (optional). Set trace_or_observation to None if trace_id is provided.
3303        """
3304        parsed_trace_id: str = None
3305        parsed_observation_id: str = None
3306
3307        if isinstance(trace_or_observation, StatefulClient):
3308            # flush the queue before creating the dataset run item
3309            # to ensure that all events are persisted.
3310            if trace_or_observation.state_type == StateType.TRACE:
3311                parsed_trace_id = trace_or_observation.trace_id
3312            elif trace_or_observation.state_type == StateType.OBSERVATION:
3313                parsed_observation_id = trace_or_observation.id
3314                parsed_trace_id = trace_or_observation.trace_id
3315        # legacy support for observation_id
3316        elif isinstance(trace_or_observation, str):
3317            parsed_observation_id = trace_or_observation
3318        elif trace_or_observation is None:
3319            if trace_id is not None:
3320                parsed_trace_id = trace_id
3321                if observation_id is not None:
3322                    parsed_observation_id = observation_id
3323            else:
3324                raise ValueError(
3325                    "trace_id must be provided if trace_or_observation is None"
3326                )
3327        else:
3328            raise ValueError(
3329                "trace_or_observation (arg) or trace_id (kwarg) must be provided to link the dataset item"
3330            )
3331
3332        self.log.debug(
3333            f"Creating dataset run item: {run_name} {self.id} {parsed_trace_id} {parsed_observation_id}"
3334        )
3335        self.langfuse.client.dataset_run_items.create(
3336            request=CreateDatasetRunItemRequest(
3337                runName=run_name,
3338                datasetItemId=self.id,
3339                traceId=parsed_trace_id,
3340                observationId=parsed_observation_id,
3341                metadata=run_metadata,
3342                runDescription=run_description,
3343            )
3344        )
3345
3346    def get_langchain_handler(
3347        self,
3348        *,
3349        run_name: str,
3350        run_description: Optional[str] = None,
3351        run_metadata: Optional[Any] = None,
3352    ):
3353        """Create and get a langchain callback handler linked to this dataset item.
3354
3355        Args:
3356            run_name (str): The name of the dataset run to be used in the callback handler.
3357            run_description (Optional[str]): Description of the dataset run.
3358            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3359
3360        Returns:
3361            CallbackHandler: An instance of CallbackHandler linked to the dataset item.
3362        """
3363        metadata = {
3364            "dataset_item_id": self.id,
3365            "run_name": run_name,
3366            "dataset_id": self.dataset_id,
3367        }
3368        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3369
3370        self.link(
3371            trace, run_name, run_metadata=run_metadata, run_description=run_description
3372        )
3373
3374        return trace.get_langchain_handler(update_parent=True)
3375
3376    @contextmanager
3377    def observe(
3378        self,
3379        *,
3380        run_name: str,
3381        run_description: Optional[str] = None,
3382        run_metadata: Optional[Any] = None,
3383        trace_id: Optional[str] = None,
3384    ):
3385        """Observes a dataset run within the Langfuse client.
3386
3387        Args:
3388            run_name (str): The name of the dataset run.
3389            root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created.
3390            run_description (Optional[str]): The description of the dataset run.
3391            run_metadata (Optional[Any]): Additional metadata for the dataset run.
3392
3393        Yields:
3394            StatefulTraceClient: The trace associated with the dataset run.
3395        """
3396        from langfuse.decorators import langfuse_context
3397
3398        root_trace_id = trace_id or str(uuid.uuid4())
3399
3400        langfuse_context._set_root_trace_id(root_trace_id)
3401
3402        try:
3403            yield root_trace_id
3404
3405        finally:
3406            self.link(
3407                run_name=run_name,
3408                run_metadata=run_metadata,
3409                run_description=run_description,
3410                trace_or_observation=None,
3411                trace_id=root_trace_id,
3412            )
3413
3414    @contextmanager
3415    def observe_llama_index(
3416        self,
3417        *,
3418        run_name: str,
3419        run_description: Optional[str] = None,
3420        run_metadata: Optional[Any] = None,
3421        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3422    ):
3423        """Context manager for observing LlamaIndex operations linked to this dataset item.
3424
3425        This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging
3426        and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all
3427        operations performed within the context are linked to the appropriate dataset item and run in Langfuse.
3428
3429        Args:
3430            run_name (str): The name of the dataset run.
3431            run_description (Optional[str]): Description of the dataset run. Defaults to None.
3432            run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
3433            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass
3434                to the LlamaIndex integration constructor. Defaults to an empty dictionary.
3435
3436        Yields:
3437            LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.
3438
3439        Example:
3440            ```python
3441            dataset_item = dataset.items[0]
3442
3443            with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler:
3444                # Perform LlamaIndex operations here
3445                some_llama_index_operation()
3446            ```
3447
3448        Raises:
3449            ImportError: If required modules for LlamaIndex integration are not available.
3450        """
3451        metadata = {
3452            "dataset_item_id": self.id,
3453            "run_name": run_name,
3454            "dataset_id": self.dataset_id,
3455        }
3456        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3457        self.link(
3458            trace, run_name, run_metadata=run_metadata, run_description=run_description
3459        )
3460
3461        try:
3462            import llama_index.core
3463            from llama_index.core import Settings
3464            from llama_index.core.callbacks import CallbackManager
3465
3466            from langfuse.llama_index import LlamaIndexCallbackHandler
3467
3468            callback_handler = LlamaIndexCallbackHandler(
3469                **llama_index_integration_constructor_kwargs,
3470            )
3471            callback_handler.set_root(trace, update_root=True)
3472
3473            # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler
3474            # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it
3475            prev_global_handler = llama_index.core.global_handler
3476            prev_langfuse_handler = None
3477
3478            if isinstance(prev_global_handler, LlamaIndexCallbackHandler):
3479                llama_index.core.global_handler = None
3480
3481            if Settings.callback_manager is None:
3482                Settings.callback_manager = CallbackManager([callback_handler])
3483            else:
3484                for handler in Settings.callback_manager.handlers:
3485                    if isinstance(handler, LlamaIndexCallbackHandler):
3486                        prev_langfuse_handler = handler
3487                        Settings.callback_manager.remove_handler(handler)
3488
3489                Settings.callback_manager.add_handler(callback_handler)
3490
3491        except Exception as e:
3492            self.log.exception(e)
3493
3494        try:
3495            yield callback_handler
3496        finally:
3497            # Reset the handlers
3498            Settings.callback_manager.remove_handler(callback_handler)
3499            if prev_langfuse_handler is not None:
3500                Settings.callback_manager.add_handler(prev_langfuse_handler)
3501
3502            llama_index.core.global_handler = prev_global_handler
3503
3504    def get_llama_index_handler(
3505        self,
3506        *,
3507        run_name: str,
3508        run_description: Optional[str] = None,
3509        run_metadata: Optional[Any] = None,
3510        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3511    ):
3512        """Create and get a llama-index callback handler linked to this dataset item.
3513
3514        Args:
3515            run_name (str): The name of the dataset run to be used in the callback handler.
3516            run_description (Optional[str]): Description of the dataset run.
3517            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3518            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
3519
3520        Returns:
3521            LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.
3522        """
3523        metadata = {
3524            "dataset_item_id": self.id,
3525            "run_name": run_name,
3526            "dataset_id": self.dataset_id,
3527        }
3528        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3529
3530        self.link(
3531            trace, run_name, run_metadata=run_metadata, run_description=run_description
3532        )
3533
3534        try:
3535            from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler
3536
3537            callback_handler = LlamaIndexCallbackHandler(
3538                **llama_index_integration_constructor_kwargs,
3539            )
3540            callback_handler.set_root(trace, update_root=True)
3541
3542            return callback_handler
3543        except Exception as e:
3544            self.log.exception(e)
3545
3546
3547class DatasetClient:
3548    """Class for managing datasets in Langfuse.
3549
3550    Attributes:
3551        id (str): Unique identifier of the dataset.
3552        name (str): Name of the dataset.
3553        description (Optional[str]): Description of the dataset.
3554        metadata (Optional[typing.Any]): Additional metadata of the dataset.
3555        project_id (str): Identifier of the project to which the dataset belongs.
3556        dataset_name (str): Name of the dataset.
3557        created_at (datetime): Timestamp of dataset creation.
3558        updated_at (datetime): Timestamp of the last update to the dataset.
3559        items (List[DatasetItemClient]): List of dataset items associated with the dataset.
3560        runs (List[str]): List of dataset runs associated with the dataset. Deprecated.
3561
3562    Example:
3563        Print the input of each dataset item in a dataset.
3564        ```python
3565        from langfuse import Langfuse
3566
3567        langfuse = Langfuse()
3568
3569        dataset = langfuse.get_dataset("<dataset_name>")
3570
3571        for item in dataset.items:
3572            print(item.input)
3573        ```
3574    """
3575
3576    id: str
3577    name: str
3578    description: Optional[str]
3579    project_id: str
3580    dataset_name: str  # for backward compatibility, to be deprecated
3581    metadata: Optional[Any]
3582    created_at: dt.datetime
3583    updated_at: dt.datetime
3584    items: typing.List[DatasetItemClient]
3585    runs: typing.List[str] = []  # deprecated
3586
3587    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3588        """Initialize the DatasetClient."""
3589        self.id = dataset.id
3590        self.name = dataset.name
3591        self.description = dataset.description
3592        self.project_id = dataset.project_id
3593        self.metadata = dataset.metadata
3594        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3595        self.created_at = dataset.created_at
3596        self.updated_at = dataset.updated_at
3597        self.items = items
3598
3599
3600def _filter_io_from_event_body(event_body: Dict[str, Any]):
3601    return {
3602        k: v for k, v in event_body.items() if k not in ("input", "output", "metadata")
3603    }
ENVIRONMENT_PATTERN = '^(?!langfuse)[a-z0-9-_]+$'
@dataclass
class FetchTracesResponse:
94@dataclass
95class FetchTracesResponse:
96    """Response object for fetch_traces method."""
97
98    data: typing.List[TraceWithDetails]
99    meta: MetaResponse

Response object for fetch_traces method.

FetchTracesResponse( data: List[langfuse.api.TraceWithDetails], meta: langfuse.api.resources.utils.resources.pagination.types.meta_response.MetaResponse)
meta: langfuse.api.resources.utils.resources.pagination.types.meta_response.MetaResponse
@dataclass
class FetchTraceResponse:
102@dataclass
103class FetchTraceResponse:
104    """Response object for fetch_trace method."""
105
106    data: TraceWithFullDetails

Response object for fetch_trace method.

FetchTraceResponse( data: langfuse.api.TraceWithFullDetails)
@dataclass
class FetchObservationsResponse:
109@dataclass
110class FetchObservationsResponse:
111    """Response object for fetch_observations method."""
112
113    data: typing.List[ObservationsView]
114    meta: MetaResponse

Response object for fetch_observations method.

FetchObservationsResponse( data: List[langfuse.api.ObservationsView], meta: langfuse.api.resources.utils.resources.pagination.types.meta_response.MetaResponse)
meta: langfuse.api.resources.utils.resources.pagination.types.meta_response.MetaResponse
@dataclass
class FetchObservationResponse:
117@dataclass
118class FetchObservationResponse:
119    """Response object for fetch_observation method."""
120
121    data: Observation

Response object for fetch_observation method.

FetchObservationResponse(data: langfuse.api.Observation)
@dataclass
class FetchMediaResponse:
124@dataclass
125class FetchMediaResponse:
126    """Response object for fetch_media method."""
127
128    data: GetMediaResponse

Response object for fetch_media method.

FetchMediaResponse( data: langfuse.api.GetMediaResponse)
@dataclass
class FetchSessionsResponse:
131@dataclass
132class FetchSessionsResponse:
133    """Response object for fetch_sessions method."""
134
135    data: typing.List[Session]
136    meta: MetaResponse

Response object for fetch_sessions method.

FetchSessionsResponse( data: List[langfuse.api.Session], meta: langfuse.api.resources.utils.resources.pagination.types.meta_response.MetaResponse)
data: List[langfuse.api.Session]
meta: langfuse.api.resources.utils.resources.pagination.types.meta_response.MetaResponse
class Langfuse:
 139class Langfuse(object):
 140    """Langfuse Python client.
 141
 142    Attributes:
 143        log (logging.Logger): Logger for the Langfuse client.
 144        base_url (str): Base URL of the Langfuse API, serving as the root address for API endpoint construction.
 145        httpx_client (httpx.Client): HTTPX client utilized for executing requests to the Langfuse API.
 146        client (FernLangfuse): Core interface for Langfuse API interaction.
 147        task_manager (TaskManager): Task Manager dedicated to handling asynchronous tasks.
 148        release (str): Identifies the release number or hash of the application.
 149        prompt_cache (PromptCache): A cache for efficiently storing and retrieving PromptClient instances.
 150
 151    Example:
 152        Initiating the Langfuse client should always be first step to use Langfuse.
 153        ```python
 154        import os
 155        from langfuse import Langfuse
 156
 157        # Set the public and secret keys as environment variables
 158        os.environ['LANGFUSE_PUBLIC_KEY'] = public_key
 159        os.environ['LANGFUSE_SECRET_KEY'] = secret_key
 160
 161        # Initialize the Langfuse client using the credentials
 162        langfuse = Langfuse()
 163        ```
 164    """
 165
 166    log = logging.getLogger("langfuse")
 167    """Logger for the Langfuse client."""
 168
 169    host: str
 170    """Host of Langfuse API."""
 171
 172    project_id: Optional[str]
 173    """Project ID of the Langfuse project associated with the API keys provided."""
 174
 175    def __init__(
 176        self,
 177        public_key: Optional[str] = None,
 178        secret_key: Optional[str] = None,
 179        host: Optional[str] = None,
 180        release: Optional[str] = None,
 181        debug: bool = False,
 182        threads: Optional[int] = None,
 183        flush_at: Optional[int] = None,
 184        flush_interval: Optional[float] = None,
 185        max_retries: Optional[int] = None,
 186        timeout: Optional[int] = None,  # seconds
 187        sdk_integration: Optional[str] = "default",
 188        httpx_client: Optional[httpx.Client] = None,
 189        enabled: Optional[bool] = True,
 190        sample_rate: Optional[float] = None,
 191        mask: Optional[MaskFunction] = None,
 192        environment: Optional[str] = None,
 193    ):
 194        """Initialize the Langfuse client.
 195
 196        Args:
 197            public_key: Public API key of Langfuse project. Can be set via `LANGFUSE_PUBLIC_KEY` environment variable.
 198            secret_key: Secret API key of Langfuse project. Can be set via `LANGFUSE_SECRET_KEY` environment variable.
 199            host: Host of Langfuse API. Can be set via `LANGFUSE_HOST` environment variable. Defaults to `https://cloud.langfuse.com`.
 200            release: Release number/hash of the application to provide analytics grouped by release. Can be set via `LANGFUSE_RELEASE` environment variable.
 201            debug: Enables debug mode for more verbose logging. Can be set via `LANGFUSE_DEBUG` environment variable.
 202            threads: Number of consumer threads to execute network requests. Helps scaling the SDK for high load. Only increase this if you run into scaling issues.
 203            flush_at: Max batch size that's sent to the API.
 204            flush_interval: Max delay until a new batch is sent to the API.
 205            max_retries: Max number of retries in case of API/network errors.
 206            timeout: Timeout of API requests in seconds. Defaults to 20 seconds.
 207            httpx_client: Pass your own httpx client for more customizability of requests.
 208            sdk_integration: Used by intgerations that wrap the Langfuse SDK to add context for debugging and support. Not to be used directly.
 209            enabled: Enables or disables the Langfuse client. If disabled, all observability calls to the backend will be no-ops.
 210            sample_rate: Sampling rate for tracing. If set to 0.2, only 20% of the data will be sent to the backend. Can be set via `LANGFUSE_SAMPLE_RATE` environment variable.
 211            mask (langfuse.types.MaskFunction): Masking function for 'input' and 'output' fields in events. Function must take a single keyword argument `data` and return a serializable, masked version of the data.
 212            environment (optional): The tracing environment. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. Can bet set via `LANGFUSE_TRACING_ENVIRONMENT` environment variable.
 213
 214        Raises:
 215            ValueError: If public_key or secret_key are not set and not found in environment variables.
 216
 217        Example:
 218            Initiating the Langfuse client should always be first step to use Langfuse.
 219            ```python
 220            import os
 221            from langfuse import Langfuse
 222
 223            # Set the public and secret keys as environment variables
 224            os.environ['LANGFUSE_PUBLIC_KEY'] = public_key
 225            os.environ['LANGFUSE_SECRET_KEY'] = secret_key
 226
 227            # Initialize the Langfuse client using the credentials
 228            langfuse = Langfuse()
 229            ```
 230        """
 231        self.enabled = enabled
 232        public_key = public_key or os.environ.get("LANGFUSE_PUBLIC_KEY")
 233        secret_key = secret_key or os.environ.get("LANGFUSE_SECRET_KEY")
 234        sample_rate = (
 235            sample_rate
 236            if sample_rate
 237            is not None  # needs explicit None check, as 0 is a valid value
 238            else float(os.environ.get("LANGFUSE_SAMPLE_RATE", 1.0))
 239        )
 240
 241        if sample_rate is not None and (
 242            sample_rate > 1 or sample_rate < 0
 243        ):  # default value 1 will be set in the taskmanager
 244            self.enabled = False
 245            self.log.warning(
 246                "Langfuse client is disabled since the sample rate provided is not between 0 and 1."
 247            )
 248
 249        threads = threads or int(os.environ.get("LANGFUSE_THREADS", 1))
 250        flush_at = flush_at or int(os.environ.get("LANGFUSE_FLUSH_AT", 15))
 251        flush_interval = flush_interval or float(
 252            os.environ.get("LANGFUSE_FLUSH_INTERVAL", 0.5)
 253        )
 254
 255        max_retries = max_retries or int(os.environ.get("LANGFUSE_MAX_RETRIES", 3))
 256        timeout = timeout or int(os.environ.get("LANGFUSE_TIMEOUT", 20))
 257
 258        if not self.enabled:
 259            self.log.warning(
 260                "Langfuse client is disabled. No observability data will be sent."
 261            )
 262
 263        elif not public_key:
 264            self.enabled = False
 265            self.log.warning(
 266                "Langfuse client is disabled since no public_key was provided as a parameter or environment variable 'LANGFUSE_PUBLIC_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client"
 267            )
 268
 269        elif not secret_key:
 270            self.enabled = False
 271            self.log.warning(
 272                "Langfuse client is disabled since no secret_key was provided as a parameter or environment variable 'LANGFUSE_SECRET_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client"
 273            )
 274
 275        set_debug = debug if debug else (os.getenv("LANGFUSE_DEBUG", "False") == "True")
 276
 277        if set_debug is True:
 278            # Ensures that debug level messages are logged when debug mode is on.
 279            # Otherwise, defaults to WARNING level.
 280            # See https://docs.python.org/3/howto/logging.html#what-happens-if-no-configuration-is-provided
 281            logging.basicConfig()
 282            # Set level for all loggers under langfuse package
 283            logging.getLogger("langfuse").setLevel(logging.DEBUG)
 284
 285            clean_logger()
 286        else:
 287            logging.getLogger("langfuse").setLevel(logging.WARNING)
 288            clean_logger()
 289
 290        self.base_url = (
 291            host
 292            if host
 293            else os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com")
 294        )
 295
 296        self.environment = environment or os.environ.get("LANGFUSE_TRACING_ENVIRONMENT")
 297
 298        if self.environment and not bool(
 299            re.match(ENVIRONMENT_PATTERN, self.environment)
 300        ):
 301            self.log.error(
 302                f'Invalid environment specified "{environment}" that does not match validation pattern ("{ENVIRONMENT_PATTERN}"). Events will be rejected by Langfuse servers.'
 303            )
 304
 305        self.httpx_client = httpx_client or httpx.Client(timeout=timeout)
 306
 307        public_api_client = FernLangfuse(
 308            base_url=self.base_url,
 309            username=public_key,
 310            password=secret_key,
 311            x_langfuse_sdk_name="python",
 312            x_langfuse_sdk_version=version,
 313            x_langfuse_public_key=public_key,
 314            httpx_client=self.httpx_client,
 315            timeout=timeout,
 316        )
 317        async_public_api_client = AsyncFernLangfuse(
 318            base_url=self.base_url,
 319            username=public_key,
 320            password=secret_key,
 321            x_langfuse_sdk_name="python",
 322            x_langfuse_sdk_version=version,
 323            x_langfuse_public_key=public_key,
 324            timeout=timeout,
 325        )
 326
 327        self.api = public_api_client
 328        self.client = public_api_client  # legacy, to be removed in next major release
 329        self.async_api = async_public_api_client
 330
 331        langfuse_client = LangfuseClient(
 332            public_key=public_key,
 333            secret_key=secret_key,
 334            base_url=self.base_url,
 335            version=version,
 336            timeout=timeout,
 337            session=self.httpx_client,
 338        )
 339
 340        args = {
 341            "threads": threads,
 342            "flush_at": flush_at,
 343            "flush_interval": flush_interval,
 344            "max_retries": max_retries,
 345            "client": langfuse_client,
 346            "api_client": self.client,
 347            "public_key": public_key,
 348            "sdk_name": "python",
 349            "sdk_version": version,
 350            "sdk_integration": sdk_integration,
 351            "enabled": self.enabled,
 352            "sample_rate": sample_rate,
 353            "mask": mask,
 354        }
 355
 356        self.task_manager = TaskManager(**args)
 357
 358        self.trace_id = None
 359        self.project_id = None
 360
 361        self.release = self._get_release_value(release)
 362
 363        self.prompt_cache = PromptCache()
 364
 365    def _get_release_value(self, release: Optional[str] = None) -> Optional[str]:
 366        if release:
 367            return release
 368        elif "LANGFUSE_RELEASE" in os.environ:
 369            return os.environ["LANGFUSE_RELEASE"]
 370        else:
 371            return get_common_release_envs()
 372
 373    def _get_project_id(self) -> Optional[str]:
 374        """Fetch and return the current project id. Persisted across requests. Returns None if no project id is found for api keys."""
 375        if not self.project_id:
 376            proj = self.client.projects.get()
 377            if not proj.data or not proj.data[0].id:
 378                return None
 379
 380            self.project_id = proj.data[0].id
 381
 382        return self.project_id
 383
 384    def get_trace_id(self) -> str:
 385        """Get the current trace id."""
 386        return self.trace_id
 387
 388    def get_trace_url(self) -> str:
 389        """Get the URL of the current trace to view it in the Langfuse UI."""
 390        project_id = self._get_project_id()
 391        if not project_id:
 392            return f"{self.base_url}/trace/{self.trace_id}"
 393
 394        return f"{self.base_url}/project/{project_id}/traces/{self.trace_id}"
 395
 396    def get_dataset(
 397        self, name: str, *, fetch_items_page_size: Optional[int] = 50
 398    ) -> "DatasetClient":
 399        """Fetch a dataset by its name.
 400
 401        Args:
 402            name (str): The name of the dataset to fetch.
 403            fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
 404
 405        Returns:
 406            DatasetClient: The dataset with the given name.
 407        """
 408        try:
 409            self.log.debug(f"Getting datasets {name}")
 410            dataset = self.client.datasets.get(dataset_name=name)
 411
 412            dataset_items = []
 413            page = 1
 414            while True:
 415                new_items = self.client.dataset_items.list(
 416                    dataset_name=self._url_encode(name),
 417                    page=page,
 418                    limit=fetch_items_page_size,
 419                )
 420                dataset_items.extend(new_items.data)
 421                if new_items.meta.total_pages <= page:
 422                    break
 423                page += 1
 424
 425            items = [DatasetItemClient(i, langfuse=self) for i in dataset_items]
 426
 427            return DatasetClient(dataset, items=items)
 428        except Exception as e:
 429            handle_fern_exception(e)
 430            raise e
 431
 432    def get_dataset_item(self, id: str) -> "DatasetItemClient":
 433        """Get the dataset item with the given id."""
 434        try:
 435            self.log.debug(f"Getting dataset item {id}")
 436            dataset_item = self.client.dataset_items.get(id=id)
 437            return DatasetItemClient(dataset_item, langfuse=self)
 438        except Exception as e:
 439            handle_fern_exception(e)
 440            raise e
 441
 442    def auth_check(self) -> bool:
 443        """Check if the provided credentials (public and secret key) are valid.
 444
 445        Raises:
 446            Exception: If no projects were found for the provided credentials.
 447
 448        Note:
 449            This method is blocking. It is discouraged to use it in production code.
 450        """
 451        try:
 452            projects = self.client.projects.get()
 453            self.log.debug(
 454                f"Auth check successful, found {len(projects.data)} projects"
 455            )
 456            if len(projects.data) == 0:
 457                raise Exception(
 458                    "Auth check failed, no project found for the keys provided."
 459                )
 460            return True
 461
 462        except Exception as e:
 463            handle_fern_exception(e)
 464            raise e
 465
 466    def get_dataset_runs(
 467        self,
 468        dataset_name: str,
 469        *,
 470        page: typing.Optional[int] = None,
 471        limit: typing.Optional[int] = None,
 472    ) -> PaginatedDatasetRuns:
 473        """Get all dataset runs.
 474
 475        Args:
 476            dataset_name (str): Name of the dataset.
 477            page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None.
 478            limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50.
 479
 480        Returns:
 481            PaginatedDatasetRuns: The dataset runs.
 482        """
 483        try:
 484            self.log.debug("Getting dataset runs")
 485            return self.client.datasets.get_runs(
 486                dataset_name=self._url_encode(dataset_name), page=page, limit=limit
 487            )
 488        except Exception as e:
 489            handle_fern_exception(e)
 490            raise e
 491
 492    def get_dataset_run(
 493        self,
 494        dataset_name: str,
 495        dataset_run_name: str,
 496    ) -> DatasetRunWithItems:
 497        """Get a dataset run.
 498
 499        Args:
 500            dataset_name: Name of the dataset.
 501            dataset_run_name: Name of the dataset run.
 502
 503        Returns:
 504            DatasetRunWithItems: The dataset run.
 505        """
 506        try:
 507            self.log.debug(
 508                f"Getting dataset runs for dataset {dataset_name} and run {dataset_run_name}"
 509            )
 510            return self.client.datasets.get_run(
 511                dataset_name=self._url_encode(dataset_name),
 512                run_name=self._url_encode(dataset_run_name),
 513            )
 514        except Exception as e:
 515            handle_fern_exception(e)
 516            raise e
 517
 518    def create_dataset(
 519        self,
 520        name: str,
 521        description: Optional[str] = None,
 522        metadata: Optional[Any] = None,
 523    ) -> Dataset:
 524        """Create a dataset with the given name on Langfuse.
 525
 526        Args:
 527            name: Name of the dataset to create.
 528            description: Description of the dataset. Defaults to None.
 529            metadata: Additional metadata. Defaults to None.
 530
 531        Returns:
 532            Dataset: The created dataset as returned by the Langfuse API.
 533        """
 534        try:
 535            body = CreateDatasetRequest(
 536                name=name, description=description, metadata=metadata
 537            )
 538            self.log.debug(f"Creating datasets {body}")
 539            return self.client.datasets.create(request=body)
 540        except Exception as e:
 541            handle_fern_exception(e)
 542            raise e
 543
 544    def create_dataset_item(
 545        self,
 546        dataset_name: str,
 547        input: Optional[Any] = None,
 548        expected_output: Optional[Any] = None,
 549        metadata: Optional[Any] = None,
 550        source_trace_id: Optional[str] = None,
 551        source_observation_id: Optional[str] = None,
 552        status: Optional[DatasetStatus] = None,
 553        id: Optional[str] = None,
 554    ) -> DatasetItem:
 555        """Create a dataset item.
 556
 557        Upserts if an item with id already exists.
 558
 559        Args:
 560            dataset_name: Name of the dataset in which the dataset item should be created.
 561            input: Input data. Defaults to None. Can contain any dict, list or scalar.
 562            expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
 563            metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
 564            source_trace_id: Id of the source trace. Defaults to None.
 565            source_observation_id: Id of the source observation. Defaults to None.
 566            status: Status of the dataset item. Defaults to ACTIVE for newly created items.
 567            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.
 568
 569        Returns:
 570            DatasetItem: The created dataset item as returned by the Langfuse API.
 571
 572        Example:
 573            ```python
 574            from langfuse import Langfuse
 575
 576            langfuse = Langfuse()
 577
 578            # Uploading items to the Langfuse dataset named "capital_cities"
 579            langfuse.create_dataset_item(
 580                dataset_name="capital_cities",
 581                input={"input": {"country": "Italy"}},
 582                expected_output={"expected_output": "Rome"},
 583                metadata={"foo": "bar"}
 584            )
 585            ```
 586        """
 587        try:
 588            body = CreateDatasetItemRequest(
 589                datasetName=dataset_name,
 590                input=input,
 591                expectedOutput=expected_output,
 592                metadata=metadata,
 593                sourceTraceId=source_trace_id,
 594                sourceObservationId=source_observation_id,
 595                status=status,
 596                id=id,
 597            )
 598            self.log.debug(f"Creating dataset item {body}")
 599            return self.client.dataset_items.create(request=body)
 600        except Exception as e:
 601            handle_fern_exception(e)
 602            raise e
 603
 604    def fetch_trace(
 605        self,
 606        id: str,
 607    ) -> FetchTraceResponse:
 608        """Fetch a trace via the Langfuse API by its id.
 609
 610        Args:
 611            id: The id of the trace to fetch.
 612
 613        Returns:
 614            FetchTraceResponse: The trace with full details as returned by the Langfuse API on `data`.
 615
 616        Raises:
 617            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
 618        """
 619        try:
 620            self.log.debug(f"Getting trace {id}")
 621            trace = self.client.trace.get(id)
 622            return FetchTraceResponse(data=trace)
 623        except Exception as e:
 624            handle_fern_exception(e)
 625            raise e
 626
 627    def get_trace(
 628        self,
 629        id: str,
 630    ) -> TraceWithFullDetails:
 631        """Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead.
 632
 633        Args:
 634            id: The id of the trace to fetch.
 635
 636        Returns:
 637            TraceWithFullDetails: The trace with full details as returned by the Langfuse API.
 638
 639        Raises:
 640            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
 641        """
 642        warnings.warn(
 643            "get_trace is deprecated, use fetch_trace instead.",
 644            DeprecationWarning,
 645        )
 646
 647        try:
 648            self.log.debug(f"Getting trace {id}")
 649            return self.client.trace.get(id)
 650        except Exception as e:
 651            handle_fern_exception(e)
 652            raise e
 653
 654    def fetch_traces(
 655        self,
 656        *,
 657        page: Optional[int] = None,
 658        limit: Optional[int] = None,
 659        user_id: Optional[str] = None,
 660        name: Optional[str] = None,
 661        session_id: Optional[str] = None,
 662        from_timestamp: Optional[dt.datetime] = None,
 663        to_timestamp: Optional[dt.datetime] = None,
 664        order_by: Optional[str] = None,
 665        tags: Optional[Union[str, Sequence[str]]] = None,
 666    ) -> FetchTracesResponse:
 667        """Fetch a list of traces in the current project matching the given parameters.
 668
 669        Args:
 670            page (Optional[int]): Page number, starts at 1. Defaults to None.
 671            limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
 672            name (Optional[str]): Filter by name of traces. Defaults to None.
 673            user_id (Optional[str]): Filter by user_id. Defaults to None.
 674            session_id (Optional[str]): Filter by session_id. Defaults to None.
 675            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
 676            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
 677            order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None.
 678            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
 679
 680        Returns:
 681            FetchTracesResponse, list of traces on `data` and metadata on `meta`.
 682
 683        Raises:
 684            Exception: If an error occurred during the request.
 685        """
 686        try:
 687            self.log.debug(
 688                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
 689            )
 690            res = self.client.trace.list(
 691                page=page,
 692                limit=limit,
 693                name=name,
 694                user_id=user_id,
 695                session_id=session_id,
 696                from_timestamp=from_timestamp,
 697                to_timestamp=to_timestamp,
 698                order_by=order_by,
 699                tags=tags,
 700            )
 701            return FetchTracesResponse(data=res.data, meta=res.meta)
 702        except Exception as e:
 703            handle_fern_exception(e)
 704            raise e
 705
 706    def get_traces(
 707        self,
 708        *,
 709        page: Optional[int] = None,
 710        limit: Optional[int] = None,
 711        user_id: Optional[str] = None,
 712        name: Optional[str] = None,
 713        session_id: Optional[str] = None,
 714        from_timestamp: Optional[dt.datetime] = None,
 715        to_timestamp: Optional[dt.datetime] = None,
 716        order_by: Optional[str] = None,
 717        tags: Optional[Union[str, Sequence[str]]] = None,
 718    ) -> Traces:
 719        """Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead.
 720
 721        Args:
 722            page (Optional[int]): Page number, starts at 1. Defaults to None.
 723            limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
 724            name (Optional[str]): Filter by name of traces. Defaults to None.
 725            user_id (Optional[str]): Filter by user_id. Defaults to None.
 726            session_id (Optional[str]): Filter by session_id. Defaults to None.
 727            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
 728            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
 729            order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None.
 730            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
 731
 732        Returns:
 733            List of Traces
 734
 735        Raises:
 736            Exception: If an error occurred during the request.
 737        """
 738        warnings.warn(
 739            "get_traces is deprecated, use fetch_traces instead.",
 740            DeprecationWarning,
 741        )
 742        try:
 743            self.log.debug(
 744                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
 745            )
 746            return self.client.trace.list(
 747                page=page,
 748                limit=limit,
 749                name=name,
 750                user_id=user_id,
 751                session_id=session_id,
 752                from_timestamp=from_timestamp,
 753                to_timestamp=to_timestamp,
 754                order_by=order_by,
 755                tags=tags,
 756            )
 757        except Exception as e:
 758            handle_fern_exception(e)
 759            raise e
 760
 761    def fetch_observations(
 762        self,
 763        *,
 764        page: typing.Optional[int] = None,
 765        limit: typing.Optional[int] = None,
 766        name: typing.Optional[str] = None,
 767        user_id: typing.Optional[str] = None,
 768        trace_id: typing.Optional[str] = None,
 769        parent_observation_id: typing.Optional[str] = None,
 770        from_start_time: typing.Optional[dt.datetime] = None,
 771        to_start_time: typing.Optional[dt.datetime] = None,
 772        type: typing.Optional[str] = None,
 773    ) -> FetchObservationsResponse:
 774        """Get a list of observations in the current project matching the given parameters.
 775
 776        Args:
 777            page (Optional[int]): Page number of the observations to return. Defaults to None.
 778            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
 779            name (Optional[str]): Name of the observations to return. Defaults to None.
 780            user_id (Optional[str]): User identifier. Defaults to None.
 781            trace_id (Optional[str]): Trace identifier. Defaults to None.
 782            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
 783            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
 784            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
 785            type (Optional[str]): Type of the observation. Defaults to None.
 786
 787        Returns:
 788            FetchObservationsResponse, list of observations on `data` and metadata on `meta`.
 789
 790        Raises:
 791            Exception: If an error occurred during the request.
 792        """
 793        try:
 794            self.log.debug(
 795                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
 796            )
 797            res = self.client.observations.get_many(
 798                page=page,
 799                limit=limit,
 800                name=name,
 801                user_id=user_id,
 802                trace_id=trace_id,
 803                parent_observation_id=parent_observation_id,
 804                from_start_time=from_start_time,
 805                to_start_time=to_start_time,
 806                type=type,
 807            )
 808            return FetchObservationsResponse(data=res.data, meta=res.meta)
 809        except Exception as e:
 810            self.log.exception(e)
 811            raise e
 812
 813    def get_observations(
 814        self,
 815        *,
 816        page: typing.Optional[int] = None,
 817        limit: typing.Optional[int] = None,
 818        name: typing.Optional[str] = None,
 819        user_id: typing.Optional[str] = None,
 820        trace_id: typing.Optional[str] = None,
 821        parent_observation_id: typing.Optional[str] = None,
 822        from_start_time: typing.Optional[dt.datetime] = None,
 823        to_start_time: typing.Optional[dt.datetime] = None,
 824        type: typing.Optional[str] = None,
 825    ) -> ObservationsViews:
 826        """Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead.
 827
 828        Args:
 829            page (Optional[int]): Page number of the observations to return. Defaults to None.
 830            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
 831            name (Optional[str]): Name of the observations to return. Defaults to None.
 832            user_id (Optional[str]): User identifier. Defaults to None.
 833            trace_id (Optional[str]): Trace identifier. Defaults to None.
 834            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
 835            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
 836            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
 837            type (Optional[str]): Type of the observation. Defaults to None.
 838
 839        Returns:
 840            List of ObservationsViews: List of observations in the project matching the given parameters.
 841
 842        Raises:
 843            Exception: If an error occurred during the request.
 844        """
 845        warnings.warn(
 846            "get_observations is deprecated, use fetch_observations instead.",
 847            DeprecationWarning,
 848        )
 849        try:
 850            self.log.debug(
 851                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
 852            )
 853            return self.client.observations.get_many(
 854                page=page,
 855                limit=limit,
 856                name=name,
 857                user_id=user_id,
 858                trace_id=trace_id,
 859                parent_observation_id=parent_observation_id,
 860                from_start_time=from_start_time,
 861                to_start_time=to_start_time,
 862                type=type,
 863            )
 864        except Exception as e:
 865            handle_fern_exception(e)
 866            raise e
 867
 868    def get_generations(
 869        self,
 870        *,
 871        page: typing.Optional[int] = None,
 872        limit: typing.Optional[int] = None,
 873        name: typing.Optional[str] = None,
 874        user_id: typing.Optional[str] = None,
 875        trace_id: typing.Optional[str] = None,
 876        from_start_time: typing.Optional[dt.datetime] = None,
 877        to_start_time: typing.Optional[dt.datetime] = None,
 878        parent_observation_id: typing.Optional[str] = None,
 879    ) -> ObservationsViews:
 880        """Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead.
 881
 882        Args:
 883            page (Optional[int]): Page number of the generations to return. Defaults to None.
 884            limit (Optional[int]): Maximum number of generations to return. Defaults to None.
 885            name (Optional[str]): Name of the generations to return. Defaults to None.
 886            user_id (Optional[str]): User identifier of the generations to return. Defaults to None.
 887            trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None.
 888            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
 889            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
 890            parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None.
 891
 892        Returns:
 893            List of ObservationsViews: List of generations in the project matching the given parameters.
 894
 895        Raises:
 896            Exception: If an error occurred during the request.
 897        """
 898        warnings.warn(
 899            "get_generations is deprecated, use `fetch_observations(type='GENERATION')` instead.",
 900            DeprecationWarning,
 901        )
 902        return self.get_observations(
 903            page=page,
 904            limit=limit,
 905            name=name,
 906            user_id=user_id,
 907            trace_id=trace_id,
 908            parent_observation_id=parent_observation_id,
 909            from_start_time=from_start_time,
 910            to_start_time=to_start_time,
 911            type="GENERATION",
 912        )
 913
 914    def fetch_observation(
 915        self,
 916        id: str,
 917    ) -> FetchObservationResponse:
 918        """Get an observation in the current project with the given identifier.
 919
 920        Args:
 921            id: The identifier of the observation to fetch.
 922
 923        Returns:
 924            FetchObservationResponse: The observation with the given id on `data`.
 925
 926        Raises:
 927            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
 928        """
 929        try:
 930            self.log.debug(f"Getting observation {id}")
 931            observation = self.client.observations.get(id)
 932            return FetchObservationResponse(data=observation)
 933        except Exception as e:
 934            handle_fern_exception(e)
 935            raise e
 936
 937    def fetch_media(self, id: str) -> FetchMediaResponse:
 938        """Get media content by ID.
 939
 940        Args:
 941            id: The identifier of the media content to fetch.
 942
 943        Returns:
 944            FetchMediaResponse: The media data of the given id on `data`.
 945
 946        Raises:
 947            Exception: If the media content with the given id could not be found within the authenticated project or if an error occurred during the request.
 948        """
 949        try:
 950            return FetchMediaResponse(data=self.client.media.get(id))
 951        except Exception as e:
 952            handle_fern_exception(e)
 953            raise e
 954
 955    def resolve_media_references(
 956        self,
 957        *,
 958        obj: Any,
 959        resolve_with: Literal["base64_data_uri"],
 960        max_depth: int = 10,
 961        content_fetch_timeout_seconds: int = 10,
 962    ):
 963        """Replace media reference strings in an object with base64 data URIs.
 964
 965        This method recursively traverses an object (up to max_depth) looking for media reference strings
 966        in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using
 967        the provided Langfuse client and replaces the reference string with a base64 data URI.
 968
 969        If fetching media content fails for a reference string, a warning is logged and the reference
 970        string is left unchanged.
 971
 972        Args:
 973            obj: The object to process. Can be a primitive value, array, or nested object.
 974                If the object has a __dict__ attribute, a dict will be returned instead of the original object type.
 975            resolve_with: The representation of the media content to replace the media reference string with.
 976                Currently only "base64_data_uri" is supported.
 977            max_depth: int: The maximum depth to traverse the object. Default is 10.
 978            content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 10.
 979
 980        Returns:
 981            A deep copy of the input object with all media references replaced with base64 data URIs where possible.
 982            If the input object has a __dict__ attribute, a dict will be returned instead of the original object type.
 983
 984        Example:
 985            obj = {
 986                "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@",
 987                "nested": {
 988                    "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@"
 989                }
 990            }
 991
 992            result = await LangfuseMedia.resolve_media_references(obj, langfuse_client)
 993
 994            # Result:
 995            # {
 996            #     "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
 997            #     "nested": {
 998            #         "pdf": "data:application/pdf;base64,JVBERi0xLjcK..."
 999            #     }
1000            # }
1001        """
1002        return LangfuseMedia.resolve_media_references(
1003            langfuse_client=self,
1004            obj=obj,
1005            resolve_with=resolve_with,
1006            max_depth=max_depth,
1007            content_fetch_timeout_seconds=content_fetch_timeout_seconds,
1008        )
1009
1010    def get_observation(
1011        self,
1012        id: str,
1013    ) -> Observation:
1014        """Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead.
1015
1016        Args:
1017            id: The identifier of the observation to fetch.
1018
1019        Raises:
1020            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
1021        """
1022        warnings.warn(
1023            "get_observation is deprecated, use fetch_observation instead.",
1024            DeprecationWarning,
1025        )
1026        try:
1027            self.log.debug(f"Getting observation {id}")
1028            return self.client.observations.get(id)
1029        except Exception as e:
1030            handle_fern_exception(e)
1031            raise e
1032
1033    def fetch_sessions(
1034        self,
1035        *,
1036        page: typing.Optional[int] = None,
1037        limit: typing.Optional[int] = None,
1038        from_timestamp: typing.Optional[dt.datetime] = None,
1039        to_timestamp: typing.Optional[dt.datetime] = None,
1040    ) -> FetchSessionsResponse:
1041        """Get a list of sessions in the current project.
1042
1043        Args:
1044            page (Optional[int]): Page number of the sessions to return. Defaults to None.
1045            limit (Optional[int]): Maximum number of sessions to return. Defaults to None.
1046            from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None.
1047            to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None.
1048
1049        Returns:
1050            FetchSessionsResponse, list of sessions on `data` and metadata on `meta`.
1051
1052        Raises:
1053            Exception: If an error occurred during the request.
1054        """
1055        try:
1056            self.log.debug(
1057                f"Getting sessions... {page}, {limit}, {from_timestamp}, {to_timestamp}"
1058            )
1059            res = self.client.sessions.list(
1060                page=page,
1061                limit=limit,
1062                from_timestamp=from_timestamp,
1063                to_timestamp=to_timestamp,
1064            )
1065            return FetchSessionsResponse(data=res.data, meta=res.meta)
1066        except Exception as e:
1067            handle_fern_exception(e)
1068            raise e
1069
1070    @overload
1071    def get_prompt(
1072        self,
1073        name: str,
1074        version: Optional[int] = None,
1075        *,
1076        label: Optional[str] = None,
1077        type: Literal["chat"],
1078        cache_ttl_seconds: Optional[int] = None,
1079        fallback: Optional[List[ChatMessageDict]] = None,
1080        max_retries: Optional[int] = None,
1081        fetch_timeout_seconds: Optional[int] = None,
1082    ) -> ChatPromptClient: ...
1083
1084    @overload
1085    def get_prompt(
1086        self,
1087        name: str,
1088        version: Optional[int] = None,
1089        *,
1090        label: Optional[str] = None,
1091        type: Literal["text"] = "text",
1092        cache_ttl_seconds: Optional[int] = None,
1093        fallback: Optional[str] = None,
1094        max_retries: Optional[int] = None,
1095        fetch_timeout_seconds: Optional[int] = None,
1096    ) -> TextPromptClient: ...
1097
1098    def get_prompt(
1099        self,
1100        name: str,
1101        version: Optional[int] = None,
1102        *,
1103        label: Optional[str] = None,
1104        type: Literal["chat", "text"] = "text",
1105        cache_ttl_seconds: Optional[int] = None,
1106        fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None,
1107        max_retries: Optional[int] = None,
1108        fetch_timeout_seconds: Optional[int] = None,
1109    ) -> PromptClient:
1110        """Get a prompt.
1111
1112        This method attempts to fetch the requested prompt from the local cache. If the prompt is not found
1113        in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again
1114        and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will
1115        return the expired prompt as a fallback.
1116
1117        Args:
1118            name (str): The name of the prompt to retrieve.
1119
1120        Keyword Args:
1121            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.
1122            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.
1123            cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a
1124            keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0.
1125            type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text".
1126            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.
1127            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.
1128            fetch_timeout_seconds: Optional[int]: The timeout in milliseconds for fetching the prompt. Defaults to the default timeout set on the SDK, which is 10 seconds per default.
1129
1130        Returns:
1131            The prompt object retrieved from the cache or directly fetched if not cached or expired of type
1132            - TextPromptClient, if type argument is 'text'.
1133            - ChatPromptClient, if type argument is 'chat'.
1134
1135        Raises:
1136            Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
1137            expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
1138        """
1139        if version is not None and label is not None:
1140            raise ValueError("Cannot specify both version and label at the same time.")
1141
1142        if not name:
1143            raise ValueError("Prompt name cannot be empty.")
1144
1145        cache_key = PromptCache.generate_cache_key(name, version=version, label=label)
1146        bounded_max_retries = self._get_bounded_max_retries(
1147            max_retries, default_max_retries=2, max_retries_upper_bound=4
1148        )
1149
1150        self.log.debug(f"Getting prompt '{cache_key}'")
1151        cached_prompt = self.prompt_cache.get(cache_key)
1152
1153        if cached_prompt is None or cache_ttl_seconds == 0:
1154            self.log.debug(
1155                f"Prompt '{cache_key}' not found in cache or caching disabled."
1156            )
1157            try:
1158                return self._fetch_prompt_and_update_cache(
1159                    name,
1160                    version=version,
1161                    label=label,
1162                    ttl_seconds=cache_ttl_seconds,
1163                    max_retries=bounded_max_retries,
1164                    fetch_timeout_seconds=fetch_timeout_seconds,
1165                )
1166            except Exception as e:
1167                if fallback:
1168                    self.log.warning(
1169                        f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}"
1170                    )
1171
1172                    fallback_client_args = {
1173                        "name": name,
1174                        "prompt": fallback,
1175                        "type": type,
1176                        "version": version or 0,
1177                        "config": {},
1178                        "labels": [label] if label else [],
1179                        "tags": [],
1180                    }
1181
1182                    if type == "text":
1183                        return TextPromptClient(
1184                            prompt=Prompt_Text(**fallback_client_args),
1185                            is_fallback=True,
1186                        )
1187
1188                    if type == "chat":
1189                        return ChatPromptClient(
1190                            prompt=Prompt_Chat(**fallback_client_args),
1191                            is_fallback=True,
1192                        )
1193
1194                raise e
1195
1196        if cached_prompt.is_expired():
1197            self.log.debug(f"Stale prompt '{cache_key}' found in cache.")
1198            try:
1199                # refresh prompt in background thread, refresh_prompt deduplicates tasks
1200                self.log.debug(f"Refreshing prompt '{cache_key}' in background.")
1201                self.prompt_cache.add_refresh_prompt_task(
1202                    cache_key,
1203                    lambda: self._fetch_prompt_and_update_cache(
1204                        name,
1205                        version=version,
1206                        label=label,
1207                        ttl_seconds=cache_ttl_seconds,
1208                        max_retries=bounded_max_retries,
1209                        fetch_timeout_seconds=fetch_timeout_seconds,
1210                    ),
1211                )
1212                self.log.debug(f"Returning stale prompt '{cache_key}' from cache.")
1213                # return stale prompt
1214                return cached_prompt.value
1215
1216            except Exception as e:
1217                self.log.warning(
1218                    f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}"
1219                )
1220                # creation of refresh prompt task failed, return stale prompt
1221                return cached_prompt.value
1222
1223        return cached_prompt.value
1224
1225    def _fetch_prompt_and_update_cache(
1226        self,
1227        name: str,
1228        *,
1229        version: Optional[int] = None,
1230        label: Optional[str] = None,
1231        ttl_seconds: Optional[int] = None,
1232        max_retries: int,
1233        fetch_timeout_seconds,
1234    ) -> PromptClient:
1235        try:
1236            cache_key = PromptCache.generate_cache_key(
1237                name, version=version, label=label
1238            )
1239
1240            self.log.debug(f"Fetching prompt '{cache_key}' from server...")
1241
1242            @backoff.on_exception(
1243                backoff.constant, Exception, max_tries=max_retries, logger=None
1244            )
1245            def fetch_prompts():
1246                return self.client.prompts.get(
1247                    self._url_encode(name),
1248                    version=version,
1249                    label=label,
1250                    request_options={
1251                        "timeout_in_seconds": fetch_timeout_seconds,
1252                    }
1253                    if fetch_timeout_seconds is not None
1254                    else None,
1255                )
1256
1257            prompt_response = fetch_prompts()
1258
1259            if prompt_response.type == "chat":
1260                prompt = ChatPromptClient(prompt_response)
1261            else:
1262                prompt = TextPromptClient(prompt_response)
1263
1264            self.prompt_cache.set(cache_key, prompt, ttl_seconds)
1265
1266            return prompt
1267
1268        except Exception as e:
1269            self.log.error(f"Error while fetching prompt '{cache_key}': {str(e)}")
1270            raise e
1271
1272    def _get_bounded_max_retries(
1273        self,
1274        max_retries: Optional[int],
1275        *,
1276        default_max_retries: int = 2,
1277        max_retries_upper_bound: int = 4,
1278    ) -> int:
1279        if max_retries is None:
1280            return default_max_retries
1281
1282        bounded_max_retries = min(
1283            max(max_retries, 0),
1284            max_retries_upper_bound,
1285        )
1286
1287        return bounded_max_retries
1288
1289    @overload
1290    def create_prompt(
1291        self,
1292        *,
1293        name: str,
1294        prompt: List[ChatMessageDict],
1295        is_active: Optional[bool] = None,  # deprecated
1296        labels: List[str] = [],
1297        tags: Optional[List[str]] = None,
1298        type: Optional[Literal["chat"]],
1299        config: Optional[Any] = None,
1300        commit_message: Optional[str] = None,
1301    ) -> ChatPromptClient: ...
1302
1303    @overload
1304    def create_prompt(
1305        self,
1306        *,
1307        name: str,
1308        prompt: str,
1309        is_active: Optional[bool] = None,  # deprecated
1310        labels: List[str] = [],
1311        tags: Optional[List[str]] = None,
1312        type: Optional[Literal["text"]] = "text",
1313        config: Optional[Any] = None,
1314        commit_message: Optional[str] = None,
1315    ) -> TextPromptClient: ...
1316
1317    def create_prompt(
1318        self,
1319        *,
1320        name: str,
1321        prompt: Union[str, List[ChatMessageDict]],
1322        is_active: Optional[bool] = None,  # deprecated
1323        labels: List[str] = [],
1324        tags: Optional[List[str]] = None,
1325        type: Optional[Literal["chat", "text"]] = "text",
1326        config: Optional[Any] = None,
1327        commit_message: Optional[str] = None,
1328    ) -> PromptClient:
1329        """Create a new prompt in Langfuse.
1330
1331        Keyword Args:
1332            name : The name of the prompt to be created.
1333            prompt : The content of the prompt to be created.
1334            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.
1335            labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label.
1336            tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt.
1337            config: Additional structured data to be saved with the prompt. Defaults to None.
1338            type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text".
1339            commit_message: Optional string describing the change.
1340
1341        Returns:
1342            TextPromptClient: The prompt if type argument is 'text'.
1343            ChatPromptClient: The prompt if type argument is 'chat'.
1344        """
1345        try:
1346            self.log.debug(f"Creating prompt {name=}, {version=}, {labels=}")
1347
1348            # Handle deprecated is_active flag
1349            if is_active:
1350                self.log.warning(
1351                    "The 'is_active' flag is deprecated and will be removed in a future release. Please use the 'production' label instead."
1352                )
1353
1354                labels = labels if "production" in labels else labels + ["production"]
1355
1356            if type == "chat":
1357                if not isinstance(prompt, list):
1358                    raise ValueError(
1359                        "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes."
1360                    )
1361                request = CreatePromptRequest_Chat(
1362                    name=name,
1363                    prompt=prompt,
1364                    labels=labels,
1365                    tags=tags,
1366                    config=config or {},
1367                    commitMessage=commit_message,
1368                    type="chat",
1369                )
1370                server_prompt = self.client.prompts.create(request=request)
1371
1372                return ChatPromptClient(prompt=server_prompt)
1373
1374            if not isinstance(prompt, str):
1375                raise ValueError("For 'text' type, 'prompt' must be a string.")
1376
1377            request = CreatePromptRequest_Text(
1378                name=name,
1379                prompt=prompt,
1380                labels=labels,
1381                tags=tags,
1382                config=config or {},
1383                commitMessage=commit_message,
1384                type="text",
1385            )
1386
1387            server_prompt = self.client.prompts.create(request=request)
1388            return TextPromptClient(prompt=server_prompt)
1389
1390        except Exception as e:
1391            handle_fern_exception(e)
1392            raise e
1393
1394    def update_prompt(
1395        self,
1396        *,
1397        name: str,
1398        version: int,
1399        new_labels: List[str] = [],
1400    ):
1401        """Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.
1402
1403        Args:
1404            name (str): The name of the prompt to update.
1405            version (int): The version number of the prompt to update.
1406            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 [].
1407
1408        Returns:
1409            Prompt: The updated prompt from the Langfuse API.
1410
1411        """
1412        updated_prompt = self.client.prompt_version.update(
1413            name=name,
1414            version=version,
1415            new_labels=new_labels,
1416        )
1417        self.prompt_cache.invalidate(name)
1418        return updated_prompt
1419
1420    def _url_encode(self, url: str) -> str:
1421        return urllib.parse.quote(url)
1422
1423    def trace(
1424        self,
1425        *,
1426        id: typing.Optional[str] = None,
1427        name: typing.Optional[str] = None,
1428        user_id: typing.Optional[str] = None,
1429        session_id: typing.Optional[str] = None,
1430        version: typing.Optional[str] = None,
1431        input: typing.Optional[typing.Any] = None,
1432        output: typing.Optional[typing.Any] = None,
1433        metadata: typing.Optional[typing.Any] = None,
1434        tags: typing.Optional[typing.List[str]] = None,
1435        timestamp: typing.Optional[dt.datetime] = None,
1436        public: typing.Optional[bool] = None,
1437        **kwargs,
1438    ) -> "StatefulTraceClient":
1439        """Create a trace.
1440
1441        Args:
1442            id: The id of the trace can be set, defaults to a random id. Set it to link traces to external systems or when creating a distributed trace. Traces are upserted on id.
1443            name: Identifier of the trace. Useful for sorting/filtering in the UI.
1444            input: The input of the trace. Can be any JSON object.
1445            output: The output of the trace. Can be any JSON object.
1446            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
1447            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
1448            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
1449            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
1450            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
1451            tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
1452            timestamp: The timestamp of the trace. Defaults to the current time if not provided.
1453            public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project.
1454            **kwargs: Additional keyword arguments that can be included in the trace.
1455
1456        Returns:
1457            StatefulTraceClient: The created trace.
1458
1459        Example:
1460            ```python
1461            from langfuse import Langfuse
1462
1463            langfuse = Langfuse()
1464
1465            trace = langfuse.trace(
1466                name="example-application",
1467                user_id="user-1234")
1468            )
1469            ```
1470        """
1471        new_id = id or str(uuid.uuid4())
1472        self.trace_id = new_id
1473        try:
1474            new_dict = {
1475                "id": new_id,
1476                "name": name,
1477                "userId": user_id,
1478                "sessionId": session_id
1479                or kwargs.get("sessionId", None),  # backward compatibility
1480                "release": self.release,
1481                "version": version,
1482                "metadata": metadata,
1483                "input": input,
1484                "output": output,
1485                "tags": tags,
1486                "timestamp": timestamp or _get_timestamp(),
1487                "public": public,
1488                "environment": self.environment,
1489            }
1490            if kwargs is not None:
1491                new_dict.update(kwargs)
1492
1493            new_body = TraceBody(**new_dict)
1494
1495            self.log.debug(f"Creating trace {_filter_io_from_event_body(new_dict)}")
1496            event = {
1497                "id": str(uuid.uuid4()),
1498                "type": "trace-create",
1499                "body": new_body,
1500            }
1501
1502            self.task_manager.add_task(
1503                event,
1504            )
1505
1506        except Exception as e:
1507            self.log.exception(e)
1508        finally:
1509            self._log_memory_usage()
1510
1511            return StatefulTraceClient(
1512                self.client,
1513                new_id,
1514                StateType.TRACE,
1515                new_id,
1516                self.task_manager,
1517                self.environment,
1518            )
1519
1520    def _log_memory_usage(self):
1521        try:
1522            is_malloc_tracing_enabled = bool(int(os.getenv("PYTHONTRACEMALLOC", 0)))
1523            report_interval = int(os.getenv("LANGFUSE_DEBUG_MEMORY_REPORT_INTERVAL", 0))
1524            top_k_items = int(os.getenv("LANGFUSE_DEBUG_MEMORY_TOP_K", 10))
1525
1526            if (
1527                not is_malloc_tracing_enabled
1528                or report_interval <= 0
1529                or round(time.monotonic()) % report_interval != 0
1530            ):
1531                return
1532
1533            snapshot = tracemalloc.take_snapshot().statistics("lineno")
1534
1535            total_memory_usage = sum([stat.size for stat in snapshot]) / 1024 / 1024
1536            memory_usage_total_items = [f"{stat}" for stat in snapshot]
1537            memory_usage_langfuse_items = [
1538                stat for stat in memory_usage_total_items if "/langfuse/" in stat
1539            ]
1540
1541            logged_memory_usage = {
1542                "all_files": [f"{stat}" for stat in memory_usage_total_items][
1543                    :top_k_items
1544                ],
1545                "langfuse_files": [f"{stat}" for stat in memory_usage_langfuse_items][
1546                    :top_k_items
1547                ],
1548                "total_usage": f"{total_memory_usage:.2f} MB",
1549                "langfuse_queue_length": self.task_manager._ingestion_queue.qsize(),
1550            }
1551
1552            self.log.debug("Memory usage: ", logged_memory_usage)
1553
1554            event = SdkLogBody(log=logged_memory_usage)
1555            self.task_manager.add_task(
1556                {
1557                    "id": str(uuid.uuid4()),
1558                    "type": "sdk-log",
1559                    "timestamp": _get_timestamp(),
1560                    "body": event.dict(),
1561                }
1562            )
1563
1564        except Exception as e:
1565            self.log.exception(e)
1566
1567    @overload
1568    def score(
1569        self,
1570        *,
1571        name: str,
1572        value: float,
1573        data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None,
1574        trace_id: typing.Optional[str] = None,
1575        id: typing.Optional[str] = None,
1576        comment: typing.Optional[str] = None,
1577        observation_id: typing.Optional[str] = None,
1578        config_id: typing.Optional[str] = None,
1579        **kwargs,
1580    ) -> "StatefulClient": ...
1581
1582    @overload
1583    def score(
1584        self,
1585        *,
1586        name: str,
1587        value: str,
1588        data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL",
1589        trace_id: typing.Optional[str] = None,
1590        id: typing.Optional[str] = None,
1591        comment: typing.Optional[str] = None,
1592        observation_id: typing.Optional[str] = None,
1593        config_id: typing.Optional[str] = None,
1594        **kwargs,
1595    ) -> "StatefulClient": ...
1596
1597    def score(
1598        self,
1599        *,
1600        name: str,
1601        value: typing.Union[float, str],
1602        data_type: typing.Optional[ScoreDataType] = None,
1603        trace_id: typing.Optional[str] = None,
1604        id: typing.Optional[str] = None,
1605        comment: typing.Optional[str] = None,
1606        observation_id: typing.Optional[str] = None,
1607        config_id: typing.Optional[str] = None,
1608        **kwargs,
1609    ) -> "StatefulClient":
1610        """Create a score attached to a trace (and optionally an observation).
1611
1612        Args:
1613            name (str): Identifier of the score.
1614            value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
1615            data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present.
1616              When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
1617            trace_id (str): The id of the trace to which the score should be attached.
1618            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
1619            comment (Optional[str]): Additional context/explanation of the score.
1620            observation_id (Optional[str]): The id of the observation to which the score should be attached.
1621            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
1622            **kwargs: Additional keyword arguments to include in the score.
1623
1624        Returns:
1625            StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided).
1626
1627        Example:
1628            ```python
1629            from langfuse import Langfuse
1630
1631            langfuse = Langfuse()
1632
1633            # Create a trace
1634            trace = langfuse.trace(name="example-application")
1635
1636            # Get id of created trace
1637            trace_id = trace.id
1638
1639            # Add score to the trace
1640            trace = langfuse.score(
1641                trace_id=trace_id,
1642                name="user-explicit-feedback",
1643                value=0.9,
1644                comment="I like how personalized the response is"
1645            )
1646            ```
1647        """
1648        trace_id = trace_id or self.trace_id or str(uuid.uuid4())
1649        new_id = id or str(uuid.uuid4())
1650        try:
1651            new_dict = {
1652                "id": new_id,
1653                "trace_id": trace_id,
1654                "observation_id": observation_id,
1655                "name": name,
1656                "value": value,
1657                "data_type": data_type,
1658                "comment": comment,
1659                "config_id": config_id,
1660                "environment": self.environment,
1661                **kwargs,
1662            }
1663
1664            self.log.debug(f"Creating score {new_dict}...")
1665            new_body = ScoreBody(**new_dict)
1666
1667            event = {
1668                "id": str(uuid.uuid4()),
1669                "type": "score-create",
1670                "body": new_body,
1671            }
1672            self.task_manager.add_task(event)
1673
1674        except Exception as e:
1675            self.log.exception(e)
1676        finally:
1677            if observation_id is not None:
1678                return StatefulClient(
1679                    self.client,
1680                    observation_id,
1681                    StateType.OBSERVATION,
1682                    trace_id,
1683                    self.task_manager,
1684                    self.environment,
1685                )
1686            else:
1687                return StatefulClient(
1688                    self.client,
1689                    new_id,
1690                    StateType.TRACE,
1691                    new_id,
1692                    self.task_manager,
1693                    self.environment,
1694                )
1695
1696    def span(
1697        self,
1698        *,
1699        id: typing.Optional[str] = None,
1700        trace_id: typing.Optional[str] = None,
1701        parent_observation_id: typing.Optional[str] = None,
1702        name: typing.Optional[str] = None,
1703        start_time: typing.Optional[dt.datetime] = None,
1704        end_time: typing.Optional[dt.datetime] = None,
1705        metadata: typing.Optional[typing.Any] = None,
1706        level: typing.Optional[SpanLevel] = None,
1707        status_message: typing.Optional[str] = None,
1708        input: typing.Optional[typing.Any] = None,
1709        output: typing.Optional[typing.Any] = None,
1710        version: typing.Optional[str] = None,
1711        **kwargs,
1712    ) -> "StatefulSpanClient":
1713        """Create a span.
1714
1715        A span represents durations of units of work in a trace.
1716        Usually, you want to add a span nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
1717
1718        If no trace_id is provided, a new trace is created just for this span.
1719
1720        Args:
1721            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
1722            trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated.
1723            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1724            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
1725            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
1726            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
1727            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
1728            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
1729            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
1730            input (Optional[dict]): The input to the span. Can be any JSON object.
1731            output (Optional[dict]): The output to the span. Can be any JSON object.
1732            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1733            **kwargs: Additional keyword arguments to include in the span.
1734
1735        Returns:
1736            StatefulSpanClient: The created span.
1737
1738        Example:
1739            ```python
1740            from langfuse import Langfuse
1741
1742            langfuse = Langfuse()
1743
1744            trace = langfuse.trace(name = "llm-feature")
1745
1746            # Create a span
1747            retrieval = langfuse.span(name = "retrieval", trace_id = trace.id)
1748
1749            # Create a nested span
1750            nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id)
1751            ```
1752        """
1753        new_span_id = id or str(uuid.uuid4())
1754        new_trace_id = trace_id or str(uuid.uuid4())
1755        self.trace_id = new_trace_id
1756        try:
1757            span_body = {
1758                "id": new_span_id,
1759                "trace_id": new_trace_id,
1760                "name": name,
1761                "start_time": start_time or _get_timestamp(),
1762                "metadata": metadata,
1763                "input": input,
1764                "output": output,
1765                "level": level,
1766                "status_message": status_message,
1767                "parent_observation_id": parent_observation_id,
1768                "version": version,
1769                "end_time": end_time,
1770                "trace": {"release": self.release},
1771                "environment": self.environment,
1772                **kwargs,
1773            }
1774
1775            if trace_id is None:
1776                self._generate_trace(new_trace_id, name or new_trace_id)
1777
1778            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
1779
1780            span_body = CreateSpanBody(**span_body)
1781
1782            event = {
1783                "id": str(uuid.uuid4()),
1784                "type": "span-create",
1785                "body": span_body,
1786            }
1787
1788            self.task_manager.add_task(event)
1789
1790        except Exception as e:
1791            self.log.exception(e)
1792        finally:
1793            self._log_memory_usage()
1794
1795            return StatefulSpanClient(
1796                self.client,
1797                new_span_id,
1798                StateType.OBSERVATION,
1799                new_trace_id,
1800                self.task_manager,
1801                self.environment,
1802            )
1803
1804    def event(
1805        self,
1806        *,
1807        id: typing.Optional[str] = None,
1808        trace_id: typing.Optional[str] = None,
1809        parent_observation_id: typing.Optional[str] = None,
1810        name: typing.Optional[str] = None,
1811        start_time: typing.Optional[dt.datetime] = None,
1812        metadata: typing.Optional[typing.Any] = None,
1813        input: typing.Optional[typing.Any] = None,
1814        output: typing.Optional[typing.Any] = None,
1815        level: typing.Optional[SpanLevel] = None,
1816        status_message: typing.Optional[str] = None,
1817        version: typing.Optional[str] = None,
1818        **kwargs,
1819    ) -> "StatefulSpanClient":
1820        """Create an event.
1821
1822        An event represents a discrete event in a trace.
1823        Usually, you want to add a event nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
1824
1825        If no trace_id is provided, a new trace is created just for this event.
1826
1827        Args:
1828            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
1829            trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event.
1830            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1831            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
1832            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
1833            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
1834            input (Optional[Any]): The input to the event. Can be any JSON object.
1835            output (Optional[Any]): The output to the event. Can be any JSON object.
1836            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
1837            status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event.
1838            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
1839            **kwargs: Additional keyword arguments to include in the event.
1840
1841        Returns:
1842            StatefulSpanClient: The created event.
1843
1844        Example:
1845            ```python
1846            from langfuse import Langfuse
1847
1848            langfuse = Langfuse()
1849
1850            trace = langfuse.trace(name = "llm-feature")
1851
1852            # Create an event
1853            retrieval = langfuse.event(name = "retrieval", trace_id = trace.id)
1854            ```
1855        """
1856        event_id = id or str(uuid.uuid4())
1857        new_trace_id = trace_id or str(uuid.uuid4())
1858        self.trace_id = new_trace_id
1859        try:
1860            event_body = {
1861                "id": event_id,
1862                "trace_id": new_trace_id,
1863                "name": name,
1864                "start_time": start_time or _get_timestamp(),
1865                "metadata": metadata,
1866                "input": input,
1867                "output": output,
1868                "level": level,
1869                "status_message": status_message,
1870                "parent_observation_id": parent_observation_id,
1871                "version": version,
1872                "trace": {"release": self.release},
1873                "environment": self.environment,
1874                **kwargs,
1875            }
1876
1877            if trace_id is None:
1878                self._generate_trace(new_trace_id, name or new_trace_id)
1879
1880            request = CreateEventBody(**event_body)
1881
1882            event = {
1883                "id": str(uuid.uuid4()),
1884                "type": "event-create",
1885                "body": request,
1886            }
1887
1888            self.log.debug(
1889                f"Creating event {_filter_io_from_event_body(event_body)} ..."
1890            )
1891            self.task_manager.add_task(event)
1892
1893        except Exception as e:
1894            self.log.exception(e)
1895        finally:
1896            return StatefulSpanClient(
1897                self.client,
1898                event_id,
1899                StateType.OBSERVATION,
1900                new_trace_id,
1901                self.task_manager,
1902                self.environment,
1903            )
1904
1905    def generation(
1906        self,
1907        *,
1908        id: typing.Optional[str] = None,
1909        trace_id: typing.Optional[str] = None,
1910        parent_observation_id: typing.Optional[str] = None,
1911        name: typing.Optional[str] = None,
1912        start_time: typing.Optional[dt.datetime] = None,
1913        end_time: typing.Optional[dt.datetime] = None,
1914        completion_start_time: typing.Optional[dt.datetime] = None,
1915        metadata: typing.Optional[typing.Any] = None,
1916        level: typing.Optional[SpanLevel] = None,
1917        status_message: typing.Optional[str] = None,
1918        version: typing.Optional[str] = None,
1919        model: typing.Optional[str] = None,
1920        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
1921        input: typing.Optional[typing.Any] = None,
1922        output: typing.Optional[typing.Any] = None,
1923        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
1924        usage_details: typing.Optional[typing.Dict[str, int]] = None,
1925        cost_details: typing.Optional[typing.Dict[str, float]] = None,
1926        prompt: typing.Optional[PromptClient] = None,
1927        **kwargs,
1928    ) -> "StatefulGenerationClient":
1929        """Create a generation.
1930
1931        A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.
1932
1933        Usually, you want to add a generation nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
1934
1935        If no trace_id is provided, a new trace is created just for this generation.
1936
1937        Args:
1938            id (Optional[str]): The id of the generation can be set, defaults to random id.
1939            trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created
1940            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1941            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
1942            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
1943            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
1944            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
1945            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
1946            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
1947            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
1948            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1949            model (Optional[str]): The name of the model used for the generation.
1950            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
1951            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
1952            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
1953            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
1954            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
1955            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
1956            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
1957            **kwargs: Additional keyword arguments to include in the generation.
1958
1959        Returns:
1960            StatefulGenerationClient: The created generation.
1961
1962        Example:
1963            ```python
1964            from langfuse import Langfuse
1965
1966            langfuse = Langfuse()
1967
1968            # Create a generation in Langfuse
1969            generation = langfuse.generation(
1970                name="summary-generation",
1971                model="gpt-3.5-turbo",
1972                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
1973                input=[{"role": "system", "content": "You are a helpful assistant."},
1974                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
1975                metadata={"interface": "whatsapp"}
1976            )
1977            ```
1978        """
1979        new_trace_id = trace_id or str(uuid.uuid4())
1980        new_generation_id = id or str(uuid.uuid4())
1981        self.trace_id = new_trace_id
1982        try:
1983            generation_body = {
1984                "id": new_generation_id,
1985                "trace_id": new_trace_id,
1986                "release": self.release,
1987                "name": name,
1988                "start_time": start_time or _get_timestamp(),
1989                "metadata": metadata,
1990                "input": input,
1991                "output": output,
1992                "level": level,
1993                "status_message": status_message,
1994                "parent_observation_id": parent_observation_id,
1995                "version": version,
1996                "end_time": end_time,
1997                "completion_start_time": completion_start_time,
1998                "model": model,
1999                "model_parameters": model_parameters,
2000                "usage": _convert_usage_input(usage) if usage is not None else None,
2001                "usage_details": usage_details,
2002                "cost_details": cost_details,
2003                "trace": {"release": self.release},
2004                "environment": self.environment,
2005                **_create_prompt_context(prompt),
2006                **kwargs,
2007            }
2008
2009            if trace_id is None:
2010                trace = {
2011                    "id": new_trace_id,
2012                    "release": self.release,
2013                    "name": name,
2014                    "environment": self.environment,
2015                }
2016                request = TraceBody(**trace)
2017
2018                event = {
2019                    "id": str(uuid.uuid4()),
2020                    "type": "trace-create",
2021                    "body": request,
2022                }
2023
2024                self.log.debug("Creating trace...")
2025
2026                self.task_manager.add_task(event)
2027
2028            self.log.debug(
2029                f"Creating generation max {_filter_io_from_event_body(generation_body)}..."
2030            )
2031            request = CreateGenerationBody(**generation_body)
2032
2033            event = {
2034                "id": str(uuid.uuid4()),
2035                "type": "generation-create",
2036                "body": request,
2037            }
2038
2039            self.task_manager.add_task(event)
2040
2041        except Exception as e:
2042            self.log.exception(e)
2043        finally:
2044            return StatefulGenerationClient(
2045                self.client,
2046                new_generation_id,
2047                StateType.OBSERVATION,
2048                new_trace_id,
2049                self.task_manager,
2050                self.environment,
2051            )
2052
2053    def _generate_trace(self, trace_id: str, name: str):
2054        trace_dict = {
2055            "id": trace_id,
2056            "release": self.release,
2057            "name": name,
2058            "environment": self.environment,
2059        }
2060
2061        trace_body = TraceBody(**trace_dict)
2062
2063        event = {
2064            "id": str(uuid.uuid4()),
2065            "type": "trace-create",
2066            "body": trace_body,
2067        }
2068
2069        self.log.debug(f"Creating trace {_filter_io_from_event_body(trace_dict)}...")
2070        self.task_manager.add_task(event)
2071
2072    def join(self):
2073        """Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter.
2074
2075        If called before flushing, consumers might terminate before sending all events to Langfuse API. This method is called at exit of the SKD, right before the Python interpreter closes.
2076        To guarantee all messages have been delivered, you still need to call flush().
2077        """
2078        try:
2079            return self.task_manager.join()
2080        except Exception as e:
2081            self.log.exception(e)
2082
2083    def flush(self):
2084        """Flush the internal event queue to the Langfuse API. It blocks until the queue is empty. It should be called when the application shuts down.
2085
2086        Example:
2087            ```python
2088            from langfuse import Langfuse
2089
2090            langfuse = Langfuse()
2091
2092            # Some operations with Langfuse
2093
2094            # Flushing all events to end Langfuse cleanly
2095            langfuse.flush()
2096            ```
2097        """
2098        try:
2099            return self.task_manager.flush()
2100        except Exception as e:
2101            self.log.exception(e)
2102
2103    def shutdown(self):
2104        """Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated.
2105
2106        This function calls flush() and join() consecutively resulting in a complete shutdown of the SDK. On success of this function, no more events will be sent to Langfuse API.
2107        As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API.
2108        """
2109        try:
2110            self.prompt_cache._task_manager.shutdown()
2111
2112            # In logging.py, a handler is attached to the httpx logger.
2113            # To avoid a memory leak on singleton reset, remove all handlers
2114            httpx_logger = logging.getLogger("httpx")
2115            for handler in httpx_logger.handlers:
2116                httpx_logger.removeHandler(handler)
2117
2118            return self.task_manager.shutdown()
2119        except Exception as e:
2120            self.log.exception(e)

Langfuse Python client.

Attributes:
  • log (logging.Logger): Logger for the Langfuse client.
  • base_url (str): Base URL of the Langfuse API, serving as the root address for API endpoint construction.
  • httpx_client (httpx.Client): HTTPX client utilized for executing requests to the Langfuse API.
  • client (FernLangfuse): Core interface for Langfuse API interaction.
  • task_manager (TaskManager): Task Manager dedicated to handling asynchronous tasks.
  • release (str): Identifies the release number or hash of the application.
  • prompt_cache (PromptCache): A cache for efficiently storing and retrieving PromptClient instances.
Example:

Initiating the Langfuse client should always be first step to use Langfuse.

import os
from langfuse import Langfuse

# Set the public and secret keys as environment variables
os.environ['LANGFUSE_PUBLIC_KEY'] = public_key
os.environ['LANGFUSE_SECRET_KEY'] = secret_key

# Initialize the Langfuse client using the credentials
langfuse = Langfuse()
Langfuse( public_key: Optional[str] = None, secret_key: Optional[str] = None, host: Optional[str] = None, release: Optional[str] = None, debug: bool = False, threads: Optional[int] = None, flush_at: Optional[int] = None, flush_interval: Optional[float] = None, max_retries: Optional[int] = None, timeout: Optional[int] = None, sdk_integration: Optional[str] = 'default', httpx_client: Optional[httpx.Client] = None, enabled: Optional[bool] = True, sample_rate: Optional[float] = None, mask: Optional[langfuse.types.MaskFunction] = None, environment: Optional[str] = None)
175    def __init__(
176        self,
177        public_key: Optional[str] = None,
178        secret_key: Optional[str] = None,
179        host: Optional[str] = None,
180        release: Optional[str] = None,
181        debug: bool = False,
182        threads: Optional[int] = None,
183        flush_at: Optional[int] = None,
184        flush_interval: Optional[float] = None,
185        max_retries: Optional[int] = None,
186        timeout: Optional[int] = None,  # seconds
187        sdk_integration: Optional[str] = "default",
188        httpx_client: Optional[httpx.Client] = None,
189        enabled: Optional[bool] = True,
190        sample_rate: Optional[float] = None,
191        mask: Optional[MaskFunction] = None,
192        environment: Optional[str] = None,
193    ):
194        """Initialize the Langfuse client.
195
196        Args:
197            public_key: Public API key of Langfuse project. Can be set via `LANGFUSE_PUBLIC_KEY` environment variable.
198            secret_key: Secret API key of Langfuse project. Can be set via `LANGFUSE_SECRET_KEY` environment variable.
199            host: Host of Langfuse API. Can be set via `LANGFUSE_HOST` environment variable. Defaults to `https://cloud.langfuse.com`.
200            release: Release number/hash of the application to provide analytics grouped by release. Can be set via `LANGFUSE_RELEASE` environment variable.
201            debug: Enables debug mode for more verbose logging. Can be set via `LANGFUSE_DEBUG` environment variable.
202            threads: Number of consumer threads to execute network requests. Helps scaling the SDK for high load. Only increase this if you run into scaling issues.
203            flush_at: Max batch size that's sent to the API.
204            flush_interval: Max delay until a new batch is sent to the API.
205            max_retries: Max number of retries in case of API/network errors.
206            timeout: Timeout of API requests in seconds. Defaults to 20 seconds.
207            httpx_client: Pass your own httpx client for more customizability of requests.
208            sdk_integration: Used by intgerations that wrap the Langfuse SDK to add context for debugging and support. Not to be used directly.
209            enabled: Enables or disables the Langfuse client. If disabled, all observability calls to the backend will be no-ops.
210            sample_rate: Sampling rate for tracing. If set to 0.2, only 20% of the data will be sent to the backend. Can be set via `LANGFUSE_SAMPLE_RATE` environment variable.
211            mask (langfuse.types.MaskFunction): Masking function for 'input' and 'output' fields in events. Function must take a single keyword argument `data` and return a serializable, masked version of the data.
212            environment (optional): The tracing environment. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. Can bet set via `LANGFUSE_TRACING_ENVIRONMENT` environment variable.
213
214        Raises:
215            ValueError: If public_key or secret_key are not set and not found in environment variables.
216
217        Example:
218            Initiating the Langfuse client should always be first step to use Langfuse.
219            ```python
220            import os
221            from langfuse import Langfuse
222
223            # Set the public and secret keys as environment variables
224            os.environ['LANGFUSE_PUBLIC_KEY'] = public_key
225            os.environ['LANGFUSE_SECRET_KEY'] = secret_key
226
227            # Initialize the Langfuse client using the credentials
228            langfuse = Langfuse()
229            ```
230        """
231        self.enabled = enabled
232        public_key = public_key or os.environ.get("LANGFUSE_PUBLIC_KEY")
233        secret_key = secret_key or os.environ.get("LANGFUSE_SECRET_KEY")
234        sample_rate = (
235            sample_rate
236            if sample_rate
237            is not None  # needs explicit None check, as 0 is a valid value
238            else float(os.environ.get("LANGFUSE_SAMPLE_RATE", 1.0))
239        )
240
241        if sample_rate is not None and (
242            sample_rate > 1 or sample_rate < 0
243        ):  # default value 1 will be set in the taskmanager
244            self.enabled = False
245            self.log.warning(
246                "Langfuse client is disabled since the sample rate provided is not between 0 and 1."
247            )
248
249        threads = threads or int(os.environ.get("LANGFUSE_THREADS", 1))
250        flush_at = flush_at or int(os.environ.get("LANGFUSE_FLUSH_AT", 15))
251        flush_interval = flush_interval or float(
252            os.environ.get("LANGFUSE_FLUSH_INTERVAL", 0.5)
253        )
254
255        max_retries = max_retries or int(os.environ.get("LANGFUSE_MAX_RETRIES", 3))
256        timeout = timeout or int(os.environ.get("LANGFUSE_TIMEOUT", 20))
257
258        if not self.enabled:
259            self.log.warning(
260                "Langfuse client is disabled. No observability data will be sent."
261            )
262
263        elif not public_key:
264            self.enabled = False
265            self.log.warning(
266                "Langfuse client is disabled since no public_key was provided as a parameter or environment variable 'LANGFUSE_PUBLIC_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client"
267            )
268
269        elif not secret_key:
270            self.enabled = False
271            self.log.warning(
272                "Langfuse client is disabled since no secret_key was provided as a parameter or environment variable 'LANGFUSE_SECRET_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client"
273            )
274
275        set_debug = debug if debug else (os.getenv("LANGFUSE_DEBUG", "False") == "True")
276
277        if set_debug is True:
278            # Ensures that debug level messages are logged when debug mode is on.
279            # Otherwise, defaults to WARNING level.
280            # See https://docs.python.org/3/howto/logging.html#what-happens-if-no-configuration-is-provided
281            logging.basicConfig()
282            # Set level for all loggers under langfuse package
283            logging.getLogger("langfuse").setLevel(logging.DEBUG)
284
285            clean_logger()
286        else:
287            logging.getLogger("langfuse").setLevel(logging.WARNING)
288            clean_logger()
289
290        self.base_url = (
291            host
292            if host
293            else os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com")
294        )
295
296        self.environment = environment or os.environ.get("LANGFUSE_TRACING_ENVIRONMENT")
297
298        if self.environment and not bool(
299            re.match(ENVIRONMENT_PATTERN, self.environment)
300        ):
301            self.log.error(
302                f'Invalid environment specified "{environment}" that does not match validation pattern ("{ENVIRONMENT_PATTERN}"). Events will be rejected by Langfuse servers.'
303            )
304
305        self.httpx_client = httpx_client or httpx.Client(timeout=timeout)
306
307        public_api_client = FernLangfuse(
308            base_url=self.base_url,
309            username=public_key,
310            password=secret_key,
311            x_langfuse_sdk_name="python",
312            x_langfuse_sdk_version=version,
313            x_langfuse_public_key=public_key,
314            httpx_client=self.httpx_client,
315            timeout=timeout,
316        )
317        async_public_api_client = AsyncFernLangfuse(
318            base_url=self.base_url,
319            username=public_key,
320            password=secret_key,
321            x_langfuse_sdk_name="python",
322            x_langfuse_sdk_version=version,
323            x_langfuse_public_key=public_key,
324            timeout=timeout,
325        )
326
327        self.api = public_api_client
328        self.client = public_api_client  # legacy, to be removed in next major release
329        self.async_api = async_public_api_client
330
331        langfuse_client = LangfuseClient(
332            public_key=public_key,
333            secret_key=secret_key,
334            base_url=self.base_url,
335            version=version,
336            timeout=timeout,
337            session=self.httpx_client,
338        )
339
340        args = {
341            "threads": threads,
342            "flush_at": flush_at,
343            "flush_interval": flush_interval,
344            "max_retries": max_retries,
345            "client": langfuse_client,
346            "api_client": self.client,
347            "public_key": public_key,
348            "sdk_name": "python",
349            "sdk_version": version,
350            "sdk_integration": sdk_integration,
351            "enabled": self.enabled,
352            "sample_rate": sample_rate,
353            "mask": mask,
354        }
355
356        self.task_manager = TaskManager(**args)
357
358        self.trace_id = None
359        self.project_id = None
360
361        self.release = self._get_release_value(release)
362
363        self.prompt_cache = PromptCache()

Initialize the Langfuse client.

Arguments:
  • public_key: Public API key of Langfuse project. Can be set via LANGFUSE_PUBLIC_KEY environment variable.
  • secret_key: Secret API key of Langfuse project. Can be set via LANGFUSE_SECRET_KEY environment variable.
  • host: Host of Langfuse API. Can be set via LANGFUSE_HOST environment variable. Defaults to https://cloud.langfuse.com.
  • release: Release number/hash of the application to provide analytics grouped by release. Can be set via LANGFUSE_RELEASE environment variable.
  • debug: Enables debug mode for more verbose logging. Can be set via LANGFUSE_DEBUG environment variable.
  • threads: Number of consumer threads to execute network requests. Helps scaling the SDK for high load. Only increase this if you run into scaling issues.
  • flush_at: Max batch size that's sent to the API.
  • flush_interval: Max delay until a new batch is sent to the API.
  • max_retries: Max number of retries in case of API/network errors.
  • timeout: Timeout of API requests in seconds. Defaults to 20 seconds.
  • httpx_client: Pass your own httpx client for more customizability of requests.
  • sdk_integration: Used by intgerations that wrap the Langfuse SDK to add context for debugging and support. Not to be used directly.
  • enabled: Enables or disables the Langfuse client. If disabled, all observability calls to the backend will be no-ops.
  • sample_rate: Sampling rate for tracing. If set to 0.2, only 20% of the data will be sent to the backend. Can be set via LANGFUSE_SAMPLE_RATE environment variable.
  • mask (langfuse.types.MaskFunction): Masking function for 'input' and 'output' fields in events. Function must take a single keyword argument data and return a serializable, masked version of the data.
  • environment (optional): The tracing environment. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'. Can bet set via LANGFUSE_TRACING_ENVIRONMENT environment variable.
Raises:
  • ValueError: If public_key or secret_key are not set and not found in environment variables.
Example:

Initiating the Langfuse client should always be first step to use Langfuse.

import os
from langfuse import Langfuse

# Set the public and secret keys as environment variables
os.environ['LANGFUSE_PUBLIC_KEY'] = public_key
os.environ['LANGFUSE_SECRET_KEY'] = secret_key

# Initialize the Langfuse client using the credentials
langfuse = Langfuse()
log = <Logger langfuse (WARNING)>

Logger for the Langfuse client.

host: str

Host of Langfuse API.

project_id: Optional[str]

Project ID of the Langfuse project associated with the API keys provided.

enabled
base_url
environment
httpx_client
api
client
async_api
task_manager
trace_id
release
prompt_cache
def get_trace_id(self) -> str:
384    def get_trace_id(self) -> str:
385        """Get the current trace id."""
386        return self.trace_id

Get the current trace id.

def get_trace_url(self) -> str:
388    def get_trace_url(self) -> str:
389        """Get the URL of the current trace to view it in the Langfuse UI."""
390        project_id = self._get_project_id()
391        if not project_id:
392            return f"{self.base_url}/trace/{self.trace_id}"
393
394        return f"{self.base_url}/project/{project_id}/traces/{self.trace_id}"

Get the URL of the current trace to view it in the Langfuse UI.

def get_dataset( self, name: str, *, fetch_items_page_size: Optional[int] = 50) -> DatasetClient:
396    def get_dataset(
397        self, name: str, *, fetch_items_page_size: Optional[int] = 50
398    ) -> "DatasetClient":
399        """Fetch a dataset by its name.
400
401        Args:
402            name (str): The name of the dataset to fetch.
403            fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
404
405        Returns:
406            DatasetClient: The dataset with the given name.
407        """
408        try:
409            self.log.debug(f"Getting datasets {name}")
410            dataset = self.client.datasets.get(dataset_name=name)
411
412            dataset_items = []
413            page = 1
414            while True:
415                new_items = self.client.dataset_items.list(
416                    dataset_name=self._url_encode(name),
417                    page=page,
418                    limit=fetch_items_page_size,
419                )
420                dataset_items.extend(new_items.data)
421                if new_items.meta.total_pages <= page:
422                    break
423                page += 1
424
425            items = [DatasetItemClient(i, langfuse=self) for i in dataset_items]
426
427            return DatasetClient(dataset, items=items)
428        except Exception as e:
429            handle_fern_exception(e)
430            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 get_dataset_item(self, id: str) -> DatasetItemClient:
432    def get_dataset_item(self, id: str) -> "DatasetItemClient":
433        """Get the dataset item with the given id."""
434        try:
435            self.log.debug(f"Getting dataset item {id}")
436            dataset_item = self.client.dataset_items.get(id=id)
437            return DatasetItemClient(dataset_item, langfuse=self)
438        except Exception as e:
439            handle_fern_exception(e)
440            raise e

Get the dataset item with the given id.

def auth_check(self) -> bool:
442    def auth_check(self) -> bool:
443        """Check if the provided credentials (public and secret key) are valid.
444
445        Raises:
446            Exception: If no projects were found for the provided credentials.
447
448        Note:
449            This method is blocking. It is discouraged to use it in production code.
450        """
451        try:
452            projects = self.client.projects.get()
453            self.log.debug(
454                f"Auth check successful, found {len(projects.data)} projects"
455            )
456            if len(projects.data) == 0:
457                raise Exception(
458                    "Auth check failed, no project found for the keys provided."
459                )
460            return True
461
462        except Exception as e:
463            handle_fern_exception(e)
464            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 get_dataset_runs( self, dataset_name: str, *, page: Optional[int] = None, limit: Optional[int] = None) -> langfuse.api.PaginatedDatasetRuns:
466    def get_dataset_runs(
467        self,
468        dataset_name: str,
469        *,
470        page: typing.Optional[int] = None,
471        limit: typing.Optional[int] = None,
472    ) -> PaginatedDatasetRuns:
473        """Get all dataset runs.
474
475        Args:
476            dataset_name (str): Name of the dataset.
477            page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None.
478            limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50.
479
480        Returns:
481            PaginatedDatasetRuns: The dataset runs.
482        """
483        try:
484            self.log.debug("Getting dataset runs")
485            return self.client.datasets.get_runs(
486                dataset_name=self._url_encode(dataset_name), page=page, limit=limit
487            )
488        except Exception as e:
489            handle_fern_exception(e)
490            raise e

Get all dataset runs.

Arguments:
  • dataset_name (str): Name of the dataset.
  • page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None.
  • limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50.
Returns:

PaginatedDatasetRuns: The dataset runs.

def get_dataset_run( self, dataset_name: str, dataset_run_name: str) -> langfuse.api.DatasetRunWithItems:
492    def get_dataset_run(
493        self,
494        dataset_name: str,
495        dataset_run_name: str,
496    ) -> DatasetRunWithItems:
497        """Get a dataset run.
498
499        Args:
500            dataset_name: Name of the dataset.
501            dataset_run_name: Name of the dataset run.
502
503        Returns:
504            DatasetRunWithItems: The dataset run.
505        """
506        try:
507            self.log.debug(
508                f"Getting dataset runs for dataset {dataset_name} and run {dataset_run_name}"
509            )
510            return self.client.datasets.get_run(
511                dataset_name=self._url_encode(dataset_name),
512                run_name=self._url_encode(dataset_run_name),
513            )
514        except Exception as e:
515            handle_fern_exception(e)
516            raise e

Get a dataset run.

Arguments:
  • dataset_name: Name of the dataset.
  • dataset_run_name: Name of the dataset run.
Returns:

DatasetRunWithItems: The dataset run.

def create_dataset( self, name: str, description: Optional[str] = None, metadata: Optional[Any] = None) -> langfuse.api.Dataset:
518    def create_dataset(
519        self,
520        name: str,
521        description: Optional[str] = None,
522        metadata: Optional[Any] = None,
523    ) -> Dataset:
524        """Create a dataset with the given name on Langfuse.
525
526        Args:
527            name: Name of the dataset to create.
528            description: Description of the dataset. Defaults to None.
529            metadata: Additional metadata. Defaults to None.
530
531        Returns:
532            Dataset: The created dataset as returned by the Langfuse API.
533        """
534        try:
535            body = CreateDatasetRequest(
536                name=name, description=description, metadata=metadata
537            )
538            self.log.debug(f"Creating datasets {body}")
539            return self.client.datasets.create(request=body)
540        except Exception as e:
541            handle_fern_exception(e)
542            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:
544    def create_dataset_item(
545        self,
546        dataset_name: str,
547        input: Optional[Any] = None,
548        expected_output: Optional[Any] = None,
549        metadata: Optional[Any] = None,
550        source_trace_id: Optional[str] = None,
551        source_observation_id: Optional[str] = None,
552        status: Optional[DatasetStatus] = None,
553        id: Optional[str] = None,
554    ) -> DatasetItem:
555        """Create a dataset item.
556
557        Upserts if an item with id already exists.
558
559        Args:
560            dataset_name: Name of the dataset in which the dataset item should be created.
561            input: Input data. Defaults to None. Can contain any dict, list or scalar.
562            expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
563            metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
564            source_trace_id: Id of the source trace. Defaults to None.
565            source_observation_id: Id of the source observation. Defaults to None.
566            status: Status of the dataset item. Defaults to ACTIVE for newly created items.
567            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.
568
569        Returns:
570            DatasetItem: The created dataset item as returned by the Langfuse API.
571
572        Example:
573            ```python
574            from langfuse import Langfuse
575
576            langfuse = Langfuse()
577
578            # Uploading items to the Langfuse dataset named "capital_cities"
579            langfuse.create_dataset_item(
580                dataset_name="capital_cities",
581                input={"input": {"country": "Italy"}},
582                expected_output={"expected_output": "Rome"},
583                metadata={"foo": "bar"}
584            )
585            ```
586        """
587        try:
588            body = CreateDatasetItemRequest(
589                datasetName=dataset_name,
590                input=input,
591                expectedOutput=expected_output,
592                metadata=metadata,
593                sourceTraceId=source_trace_id,
594                sourceObservationId=source_observation_id,
595                status=status,
596                id=id,
597            )
598            self.log.debug(f"Creating dataset item {body}")
599            return self.client.dataset_items.create(request=body)
600        except Exception as e:
601            handle_fern_exception(e)
602            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 fetch_trace(self, id: str) -> FetchTraceResponse:
604    def fetch_trace(
605        self,
606        id: str,
607    ) -> FetchTraceResponse:
608        """Fetch a trace via the Langfuse API by its id.
609
610        Args:
611            id: The id of the trace to fetch.
612
613        Returns:
614            FetchTraceResponse: The trace with full details as returned by the Langfuse API on `data`.
615
616        Raises:
617            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
618        """
619        try:
620            self.log.debug(f"Getting trace {id}")
621            trace = self.client.trace.get(id)
622            return FetchTraceResponse(data=trace)
623        except Exception as e:
624            handle_fern_exception(e)
625            raise e

Fetch a trace via the Langfuse API by its id.

Arguments:
  • id: The id of the trace to fetch.
Returns:

FetchTraceResponse: The trace with full details as returned by the Langfuse API on data.

Raises:
  • Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
def get_trace( self, id: str) -> langfuse.api.TraceWithFullDetails:
627    def get_trace(
628        self,
629        id: str,
630    ) -> TraceWithFullDetails:
631        """Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead.
632
633        Args:
634            id: The id of the trace to fetch.
635
636        Returns:
637            TraceWithFullDetails: The trace with full details as returned by the Langfuse API.
638
639        Raises:
640            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
641        """
642        warnings.warn(
643            "get_trace is deprecated, use fetch_trace instead.",
644            DeprecationWarning,
645        )
646
647        try:
648            self.log.debug(f"Getting trace {id}")
649            return self.client.trace.get(id)
650        except Exception as e:
651            handle_fern_exception(e)
652            raise e

Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead.

Arguments:
  • id: The id of the trace to fetch.
Returns:

TraceWithFullDetails: The trace with full details as returned by the Langfuse API.

Raises:
  • Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
def fetch_traces( self, *, page: Optional[int] = None, limit: Optional[int] = None, user_id: Optional[str] = None, name: Optional[str] = None, session_id: Optional[str] = None, from_timestamp: Optional[datetime.datetime] = None, to_timestamp: Optional[datetime.datetime] = None, order_by: Optional[str] = None, tags: Union[str, Sequence[str], NoneType] = None) -> FetchTracesResponse:
654    def fetch_traces(
655        self,
656        *,
657        page: Optional[int] = None,
658        limit: Optional[int] = None,
659        user_id: Optional[str] = None,
660        name: Optional[str] = None,
661        session_id: Optional[str] = None,
662        from_timestamp: Optional[dt.datetime] = None,
663        to_timestamp: Optional[dt.datetime] = None,
664        order_by: Optional[str] = None,
665        tags: Optional[Union[str, Sequence[str]]] = None,
666    ) -> FetchTracesResponse:
667        """Fetch a list of traces in the current project matching the given parameters.
668
669        Args:
670            page (Optional[int]): Page number, starts at 1. Defaults to None.
671            limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
672            name (Optional[str]): Filter by name of traces. Defaults to None.
673            user_id (Optional[str]): Filter by user_id. Defaults to None.
674            session_id (Optional[str]): Filter by session_id. Defaults to None.
675            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
676            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
677            order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None.
678            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
679
680        Returns:
681            FetchTracesResponse, list of traces on `data` and metadata on `meta`.
682
683        Raises:
684            Exception: If an error occurred during the request.
685        """
686        try:
687            self.log.debug(
688                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
689            )
690            res = self.client.trace.list(
691                page=page,
692                limit=limit,
693                name=name,
694                user_id=user_id,
695                session_id=session_id,
696                from_timestamp=from_timestamp,
697                to_timestamp=to_timestamp,
698                order_by=order_by,
699                tags=tags,
700            )
701            return FetchTracesResponse(data=res.data, meta=res.meta)
702        except Exception as e:
703            handle_fern_exception(e)
704            raise e

Fetch a list of traces in the current project matching the given parameters.

Arguments:
  • page (Optional[int]): Page number, starts at 1. Defaults to None.
  • limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
  • name (Optional[str]): Filter by name of traces. Defaults to None.
  • user_id (Optional[str]): Filter by user_id. Defaults to None.
  • session_id (Optional[str]): Filter by session_id. Defaults to None.
  • from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
  • to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
  • order_by (Optional[str]): Format of the string [field].[asc/desc]. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: timestamp.asc. Defaults to None.
  • tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
Returns:

FetchTracesResponse, list of traces on data and metadata on meta.

Raises:
  • Exception: If an error occurred during the request.
def get_traces( self, *, page: Optional[int] = None, limit: Optional[int] = None, user_id: Optional[str] = None, name: Optional[str] = None, session_id: Optional[str] = None, from_timestamp: Optional[datetime.datetime] = None, to_timestamp: Optional[datetime.datetime] = None, order_by: Optional[str] = None, tags: Union[str, Sequence[str], NoneType] = None) -> langfuse.api.Traces:
706    def get_traces(
707        self,
708        *,
709        page: Optional[int] = None,
710        limit: Optional[int] = None,
711        user_id: Optional[str] = None,
712        name: Optional[str] = None,
713        session_id: Optional[str] = None,
714        from_timestamp: Optional[dt.datetime] = None,
715        to_timestamp: Optional[dt.datetime] = None,
716        order_by: Optional[str] = None,
717        tags: Optional[Union[str, Sequence[str]]] = None,
718    ) -> Traces:
719        """Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead.
720
721        Args:
722            page (Optional[int]): Page number, starts at 1. Defaults to None.
723            limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
724            name (Optional[str]): Filter by name of traces. Defaults to None.
725            user_id (Optional[str]): Filter by user_id. Defaults to None.
726            session_id (Optional[str]): Filter by session_id. Defaults to None.
727            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
728            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
729            order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None.
730            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
731
732        Returns:
733            List of Traces
734
735        Raises:
736            Exception: If an error occurred during the request.
737        """
738        warnings.warn(
739            "get_traces is deprecated, use fetch_traces instead.",
740            DeprecationWarning,
741        )
742        try:
743            self.log.debug(
744                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
745            )
746            return self.client.trace.list(
747                page=page,
748                limit=limit,
749                name=name,
750                user_id=user_id,
751                session_id=session_id,
752                from_timestamp=from_timestamp,
753                to_timestamp=to_timestamp,
754                order_by=order_by,
755                tags=tags,
756            )
757        except Exception as e:
758            handle_fern_exception(e)
759            raise e

Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead.

Arguments:
  • page (Optional[int]): Page number, starts at 1. Defaults to None.
  • limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
  • name (Optional[str]): Filter by name of traces. Defaults to None.
  • user_id (Optional[str]): Filter by user_id. Defaults to None.
  • session_id (Optional[str]): Filter by session_id. Defaults to None.
  • from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
  • to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
  • order_by (Optional[str]): Format of the string [field].[asc/desc]. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: timestamp.asc. Defaults to None.
  • tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
Returns:

List of Traces

Raises:
  • Exception: If an error occurred during the request.
def fetch_observations( self, *, page: Optional[int] = None, limit: Optional[int] = None, name: Optional[str] = None, user_id: Optional[str] = None, trace_id: Optional[str] = None, parent_observation_id: Optional[str] = None, from_start_time: Optional[datetime.datetime] = None, to_start_time: Optional[datetime.datetime] = None, type: Optional[str] = None) -> FetchObservationsResponse:
761    def fetch_observations(
762        self,
763        *,
764        page: typing.Optional[int] = None,
765        limit: typing.Optional[int] = None,
766        name: typing.Optional[str] = None,
767        user_id: typing.Optional[str] = None,
768        trace_id: typing.Optional[str] = None,
769        parent_observation_id: typing.Optional[str] = None,
770        from_start_time: typing.Optional[dt.datetime] = None,
771        to_start_time: typing.Optional[dt.datetime] = None,
772        type: typing.Optional[str] = None,
773    ) -> FetchObservationsResponse:
774        """Get a list of observations in the current project matching the given parameters.
775
776        Args:
777            page (Optional[int]): Page number of the observations to return. Defaults to None.
778            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
779            name (Optional[str]): Name of the observations to return. Defaults to None.
780            user_id (Optional[str]): User identifier. Defaults to None.
781            trace_id (Optional[str]): Trace identifier. Defaults to None.
782            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
783            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
784            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
785            type (Optional[str]): Type of the observation. Defaults to None.
786
787        Returns:
788            FetchObservationsResponse, list of observations on `data` and metadata on `meta`.
789
790        Raises:
791            Exception: If an error occurred during the request.
792        """
793        try:
794            self.log.debug(
795                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
796            )
797            res = self.client.observations.get_many(
798                page=page,
799                limit=limit,
800                name=name,
801                user_id=user_id,
802                trace_id=trace_id,
803                parent_observation_id=parent_observation_id,
804                from_start_time=from_start_time,
805                to_start_time=to_start_time,
806                type=type,
807            )
808            return FetchObservationsResponse(data=res.data, meta=res.meta)
809        except Exception as e:
810            self.log.exception(e)
811            raise e

Get a list of observations in the current project matching the given parameters.

Arguments:
  • page (Optional[int]): Page number of the observations to return. Defaults to None.
  • limit (Optional[int]): Maximum number of observations to return. Defaults to None.
  • name (Optional[str]): Name of the observations to return. Defaults to None.
  • user_id (Optional[str]): User identifier. Defaults to None.
  • trace_id (Optional[str]): Trace identifier. Defaults to None.
  • parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
  • from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
  • to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
  • type (Optional[str]): Type of the observation. Defaults to None.
Returns:

FetchObservationsResponse, list of observations on data and metadata on meta.

Raises:
  • Exception: If an error occurred during the request.
def get_observations( self, *, page: Optional[int] = None, limit: Optional[int] = None, name: Optional[str] = None, user_id: Optional[str] = None, trace_id: Optional[str] = None, parent_observation_id: Optional[str] = None, from_start_time: Optional[datetime.datetime] = None, to_start_time: Optional[datetime.datetime] = None, type: Optional[str] = None) -> langfuse.api.ObservationsViews:
813    def get_observations(
814        self,
815        *,
816        page: typing.Optional[int] = None,
817        limit: typing.Optional[int] = None,
818        name: typing.Optional[str] = None,
819        user_id: typing.Optional[str] = None,
820        trace_id: typing.Optional[str] = None,
821        parent_observation_id: typing.Optional[str] = None,
822        from_start_time: typing.Optional[dt.datetime] = None,
823        to_start_time: typing.Optional[dt.datetime] = None,
824        type: typing.Optional[str] = None,
825    ) -> ObservationsViews:
826        """Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead.
827
828        Args:
829            page (Optional[int]): Page number of the observations to return. Defaults to None.
830            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
831            name (Optional[str]): Name of the observations to return. Defaults to None.
832            user_id (Optional[str]): User identifier. Defaults to None.
833            trace_id (Optional[str]): Trace identifier. Defaults to None.
834            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
835            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
836            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
837            type (Optional[str]): Type of the observation. Defaults to None.
838
839        Returns:
840            List of ObservationsViews: List of observations in the project matching the given parameters.
841
842        Raises:
843            Exception: If an error occurred during the request.
844        """
845        warnings.warn(
846            "get_observations is deprecated, use fetch_observations instead.",
847            DeprecationWarning,
848        )
849        try:
850            self.log.debug(
851                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
852            )
853            return self.client.observations.get_many(
854                page=page,
855                limit=limit,
856                name=name,
857                user_id=user_id,
858                trace_id=trace_id,
859                parent_observation_id=parent_observation_id,
860                from_start_time=from_start_time,
861                to_start_time=to_start_time,
862                type=type,
863            )
864        except Exception as e:
865            handle_fern_exception(e)
866            raise e

Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead.

Arguments:
  • page (Optional[int]): Page number of the observations to return. Defaults to None.
  • limit (Optional[int]): Maximum number of observations to return. Defaults to None.
  • name (Optional[str]): Name of the observations to return. Defaults to None.
  • user_id (Optional[str]): User identifier. Defaults to None.
  • trace_id (Optional[str]): Trace identifier. Defaults to None.
  • parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
  • from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
  • to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
  • type (Optional[str]): Type of the observation. Defaults to None.
Returns:

List of ObservationsViews: List of observations in the project matching the given parameters.

Raises:
  • Exception: If an error occurred during the request.
def get_generations( self, *, page: Optional[int] = None, limit: Optional[int] = None, name: Optional[str] = None, user_id: Optional[str] = None, trace_id: Optional[str] = None, from_start_time: Optional[datetime.datetime] = None, to_start_time: Optional[datetime.datetime] = None, parent_observation_id: Optional[str] = None) -> langfuse.api.ObservationsViews:
868    def get_generations(
869        self,
870        *,
871        page: typing.Optional[int] = None,
872        limit: typing.Optional[int] = None,
873        name: typing.Optional[str] = None,
874        user_id: typing.Optional[str] = None,
875        trace_id: typing.Optional[str] = None,
876        from_start_time: typing.Optional[dt.datetime] = None,
877        to_start_time: typing.Optional[dt.datetime] = None,
878        parent_observation_id: typing.Optional[str] = None,
879    ) -> ObservationsViews:
880        """Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead.
881
882        Args:
883            page (Optional[int]): Page number of the generations to return. Defaults to None.
884            limit (Optional[int]): Maximum number of generations to return. Defaults to None.
885            name (Optional[str]): Name of the generations to return. Defaults to None.
886            user_id (Optional[str]): User identifier of the generations to return. Defaults to None.
887            trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None.
888            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
889            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
890            parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None.
891
892        Returns:
893            List of ObservationsViews: List of generations in the project matching the given parameters.
894
895        Raises:
896            Exception: If an error occurred during the request.
897        """
898        warnings.warn(
899            "get_generations is deprecated, use `fetch_observations(type='GENERATION')` instead.",
900            DeprecationWarning,
901        )
902        return self.get_observations(
903            page=page,
904            limit=limit,
905            name=name,
906            user_id=user_id,
907            trace_id=trace_id,
908            parent_observation_id=parent_observation_id,
909            from_start_time=from_start_time,
910            to_start_time=to_start_time,
911            type="GENERATION",
912        )

Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead.

Arguments:
  • page (Optional[int]): Page number of the generations to return. Defaults to None.
  • limit (Optional[int]): Maximum number of generations to return. Defaults to None.
  • name (Optional[str]): Name of the generations to return. Defaults to None.
  • user_id (Optional[str]): User identifier of the generations to return. Defaults to None.
  • trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None.
  • from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
  • to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
  • parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None.
Returns:

List of ObservationsViews: List of generations in the project matching the given parameters.

Raises:
  • Exception: If an error occurred during the request.
def fetch_observation(self, id: str) -> FetchObservationResponse:
914    def fetch_observation(
915        self,
916        id: str,
917    ) -> FetchObservationResponse:
918        """Get an observation in the current project with the given identifier.
919
920        Args:
921            id: The identifier of the observation to fetch.
922
923        Returns:
924            FetchObservationResponse: The observation with the given id on `data`.
925
926        Raises:
927            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
928        """
929        try:
930            self.log.debug(f"Getting observation {id}")
931            observation = self.client.observations.get(id)
932            return FetchObservationResponse(data=observation)
933        except Exception as e:
934            handle_fern_exception(e)
935            raise e

Get an observation in the current project with the given identifier.

Arguments:
  • id: The identifier of the observation to fetch.
Returns:

FetchObservationResponse: The observation with the given id on data.

Raises:
  • Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
def fetch_media(self, id: str) -> FetchMediaResponse:
937    def fetch_media(self, id: str) -> FetchMediaResponse:
938        """Get media content by ID.
939
940        Args:
941            id: The identifier of the media content to fetch.
942
943        Returns:
944            FetchMediaResponse: The media data of the given id on `data`.
945
946        Raises:
947            Exception: If the media content with the given id could not be found within the authenticated project or if an error occurred during the request.
948        """
949        try:
950            return FetchMediaResponse(data=self.client.media.get(id))
951        except Exception as e:
952            handle_fern_exception(e)
953            raise e

Get media content by ID.

Arguments:
  • id: The identifier of the media content to fetch.
Returns:

FetchMediaResponse: The media data of the given id on data.

Raises:
  • Exception: If the media content with the given id could not be found within the authenticated project or if an error occurred during the request.
def resolve_media_references( self, *, obj: Any, resolve_with: Literal['base64_data_uri'], max_depth: int = 10, content_fetch_timeout_seconds: int = 10):
 955    def resolve_media_references(
 956        self,
 957        *,
 958        obj: Any,
 959        resolve_with: Literal["base64_data_uri"],
 960        max_depth: int = 10,
 961        content_fetch_timeout_seconds: int = 10,
 962    ):
 963        """Replace media reference strings in an object with base64 data URIs.
 964
 965        This method recursively traverses an object (up to max_depth) looking for media reference strings
 966        in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using
 967        the provided Langfuse client and replaces the reference string with a base64 data URI.
 968
 969        If fetching media content fails for a reference string, a warning is logged and the reference
 970        string is left unchanged.
 971
 972        Args:
 973            obj: The object to process. Can be a primitive value, array, or nested object.
 974                If the object has a __dict__ attribute, a dict will be returned instead of the original object type.
 975            resolve_with: The representation of the media content to replace the media reference string with.
 976                Currently only "base64_data_uri" is supported.
 977            max_depth: int: The maximum depth to traverse the object. Default is 10.
 978            content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 10.
 979
 980        Returns:
 981            A deep copy of the input object with all media references replaced with base64 data URIs where possible.
 982            If the input object has a __dict__ attribute, a dict will be returned instead of the original object type.
 983
 984        Example:
 985            obj = {
 986                "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@",
 987                "nested": {
 988                    "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@"
 989                }
 990            }
 991
 992            result = await LangfuseMedia.resolve_media_references(obj, langfuse_client)
 993
 994            # Result:
 995            # {
 996            #     "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
 997            #     "nested": {
 998            #         "pdf": "data:application/pdf;base64,JVBERi0xLjcK..."
 999            #     }
1000            # }
1001        """
1002        return LangfuseMedia.resolve_media_references(
1003            langfuse_client=self,
1004            obj=obj,
1005            resolve_with=resolve_with,
1006            max_depth=max_depth,
1007            content_fetch_timeout_seconds=content_fetch_timeout_seconds,
1008        )

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 10.
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_observation( self, id: str) -> langfuse.api.Observation:
1010    def get_observation(
1011        self,
1012        id: str,
1013    ) -> Observation:
1014        """Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead.
1015
1016        Args:
1017            id: The identifier of the observation to fetch.
1018
1019        Raises:
1020            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
1021        """
1022        warnings.warn(
1023            "get_observation is deprecated, use fetch_observation instead.",
1024            DeprecationWarning,
1025        )
1026        try:
1027            self.log.debug(f"Getting observation {id}")
1028            return self.client.observations.get(id)
1029        except Exception as e:
1030            handle_fern_exception(e)
1031            raise e

Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead.

Arguments:
  • id: The identifier of the observation to fetch.
Raises:
  • Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
def fetch_sessions( self, *, page: Optional[int] = None, limit: Optional[int] = None, from_timestamp: Optional[datetime.datetime] = None, to_timestamp: Optional[datetime.datetime] = None) -> FetchSessionsResponse:
1033    def fetch_sessions(
1034        self,
1035        *,
1036        page: typing.Optional[int] = None,
1037        limit: typing.Optional[int] = None,
1038        from_timestamp: typing.Optional[dt.datetime] = None,
1039        to_timestamp: typing.Optional[dt.datetime] = None,
1040    ) -> FetchSessionsResponse:
1041        """Get a list of sessions in the current project.
1042
1043        Args:
1044            page (Optional[int]): Page number of the sessions to return. Defaults to None.
1045            limit (Optional[int]): Maximum number of sessions to return. Defaults to None.
1046            from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None.
1047            to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None.
1048
1049        Returns:
1050            FetchSessionsResponse, list of sessions on `data` and metadata on `meta`.
1051
1052        Raises:
1053            Exception: If an error occurred during the request.
1054        """
1055        try:
1056            self.log.debug(
1057                f"Getting sessions... {page}, {limit}, {from_timestamp}, {to_timestamp}"
1058            )
1059            res = self.client.sessions.list(
1060                page=page,
1061                limit=limit,
1062                from_timestamp=from_timestamp,
1063                to_timestamp=to_timestamp,
1064            )
1065            return FetchSessionsResponse(data=res.data, meta=res.meta)
1066        except Exception as e:
1067            handle_fern_exception(e)
1068            raise e

Get a list of sessions in the current project.

Arguments:
  • page (Optional[int]): Page number of the sessions to return. Defaults to None.
  • limit (Optional[int]): Maximum number of sessions to return. Defaults to None.
  • from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None.
  • to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None.
Returns:

FetchSessionsResponse, list of sessions on data and metadata on meta.

Raises:
  • Exception: If an error occurred during the request.
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]:
1098    def get_prompt(
1099        self,
1100        name: str,
1101        version: Optional[int] = None,
1102        *,
1103        label: Optional[str] = None,
1104        type: Literal["chat", "text"] = "text",
1105        cache_ttl_seconds: Optional[int] = None,
1106        fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None,
1107        max_retries: Optional[int] = None,
1108        fetch_timeout_seconds: Optional[int] = None,
1109    ) -> PromptClient:
1110        """Get a prompt.
1111
1112        This method attempts to fetch the requested prompt from the local cache. If the prompt is not found
1113        in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again
1114        and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will
1115        return the expired prompt as a fallback.
1116
1117        Args:
1118            name (str): The name of the prompt to retrieve.
1119
1120        Keyword Args:
1121            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.
1122            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.
1123            cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a
1124            keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0.
1125            type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text".
1126            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.
1127            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.
1128            fetch_timeout_seconds: Optional[int]: The timeout in milliseconds for fetching the prompt. Defaults to the default timeout set on the SDK, which is 10 seconds per default.
1129
1130        Returns:
1131            The prompt object retrieved from the cache or directly fetched if not cached or expired of type
1132            - TextPromptClient, if type argument is 'text'.
1133            - ChatPromptClient, if type argument is 'chat'.
1134
1135        Raises:
1136            Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
1137            expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
1138        """
1139        if version is not None and label is not None:
1140            raise ValueError("Cannot specify both version and label at the same time.")
1141
1142        if not name:
1143            raise ValueError("Prompt name cannot be empty.")
1144
1145        cache_key = PromptCache.generate_cache_key(name, version=version, label=label)
1146        bounded_max_retries = self._get_bounded_max_retries(
1147            max_retries, default_max_retries=2, max_retries_upper_bound=4
1148        )
1149
1150        self.log.debug(f"Getting prompt '{cache_key}'")
1151        cached_prompt = self.prompt_cache.get(cache_key)
1152
1153        if cached_prompt is None or cache_ttl_seconds == 0:
1154            self.log.debug(
1155                f"Prompt '{cache_key}' not found in cache or caching disabled."
1156            )
1157            try:
1158                return self._fetch_prompt_and_update_cache(
1159                    name,
1160                    version=version,
1161                    label=label,
1162                    ttl_seconds=cache_ttl_seconds,
1163                    max_retries=bounded_max_retries,
1164                    fetch_timeout_seconds=fetch_timeout_seconds,
1165                )
1166            except Exception as e:
1167                if fallback:
1168                    self.log.warning(
1169                        f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}"
1170                    )
1171
1172                    fallback_client_args = {
1173                        "name": name,
1174                        "prompt": fallback,
1175                        "type": type,
1176                        "version": version or 0,
1177                        "config": {},
1178                        "labels": [label] if label else [],
1179                        "tags": [],
1180                    }
1181
1182                    if type == "text":
1183                        return TextPromptClient(
1184                            prompt=Prompt_Text(**fallback_client_args),
1185                            is_fallback=True,
1186                        )
1187
1188                    if type == "chat":
1189                        return ChatPromptClient(
1190                            prompt=Prompt_Chat(**fallback_client_args),
1191                            is_fallback=True,
1192                        )
1193
1194                raise e
1195
1196        if cached_prompt.is_expired():
1197            self.log.debug(f"Stale prompt '{cache_key}' found in cache.")
1198            try:
1199                # refresh prompt in background thread, refresh_prompt deduplicates tasks
1200                self.log.debug(f"Refreshing prompt '{cache_key}' in background.")
1201                self.prompt_cache.add_refresh_prompt_task(
1202                    cache_key,
1203                    lambda: self._fetch_prompt_and_update_cache(
1204                        name,
1205                        version=version,
1206                        label=label,
1207                        ttl_seconds=cache_ttl_seconds,
1208                        max_retries=bounded_max_retries,
1209                        fetch_timeout_seconds=fetch_timeout_seconds,
1210                    ),
1211                )
1212                self.log.debug(f"Returning stale prompt '{cache_key}' from cache.")
1213                # return stale prompt
1214                return cached_prompt.value
1215
1216            except Exception as e:
1217                self.log.warning(
1218                    f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}"
1219                )
1220                # creation of refresh prompt task failed, return stale prompt
1221                return cached_prompt.value
1222
1223        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 10 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[langfuse.model.ChatMessageDict]], is_active: Optional[bool] = None, 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]:
1317    def create_prompt(
1318        self,
1319        *,
1320        name: str,
1321        prompt: Union[str, List[ChatMessageDict]],
1322        is_active: Optional[bool] = None,  # deprecated
1323        labels: List[str] = [],
1324        tags: Optional[List[str]] = None,
1325        type: Optional[Literal["chat", "text"]] = "text",
1326        config: Optional[Any] = None,
1327        commit_message: Optional[str] = None,
1328    ) -> PromptClient:
1329        """Create a new prompt in Langfuse.
1330
1331        Keyword Args:
1332            name : The name of the prompt to be created.
1333            prompt : The content of the prompt to be created.
1334            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.
1335            labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label.
1336            tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt.
1337            config: Additional structured data to be saved with the prompt. Defaults to None.
1338            type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text".
1339            commit_message: Optional string describing the change.
1340
1341        Returns:
1342            TextPromptClient: The prompt if type argument is 'text'.
1343            ChatPromptClient: The prompt if type argument is 'chat'.
1344        """
1345        try:
1346            self.log.debug(f"Creating prompt {name=}, {version=}, {labels=}")
1347
1348            # Handle deprecated is_active flag
1349            if is_active:
1350                self.log.warning(
1351                    "The 'is_active' flag is deprecated and will be removed in a future release. Please use the 'production' label instead."
1352                )
1353
1354                labels = labels if "production" in labels else labels + ["production"]
1355
1356            if type == "chat":
1357                if not isinstance(prompt, list):
1358                    raise ValueError(
1359                        "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes."
1360                    )
1361                request = CreatePromptRequest_Chat(
1362                    name=name,
1363                    prompt=prompt,
1364                    labels=labels,
1365                    tags=tags,
1366                    config=config or {},
1367                    commitMessage=commit_message,
1368                    type="chat",
1369                )
1370                server_prompt = self.client.prompts.create(request=request)
1371
1372                return ChatPromptClient(prompt=server_prompt)
1373
1374            if not isinstance(prompt, str):
1375                raise ValueError("For 'text' type, 'prompt' must be a string.")
1376
1377            request = CreatePromptRequest_Text(
1378                name=name,
1379                prompt=prompt,
1380                labels=labels,
1381                tags=tags,
1382                config=config or {},
1383                commitMessage=commit_message,
1384                type="text",
1385            )
1386
1387            server_prompt = self.client.prompts.create(request=request)
1388            return TextPromptClient(prompt=server_prompt)
1389
1390        except Exception as e:
1391            handle_fern_exception(e)
1392            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] = []):
1394    def update_prompt(
1395        self,
1396        *,
1397        name: str,
1398        version: int,
1399        new_labels: List[str] = [],
1400    ):
1401        """Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.
1402
1403        Args:
1404            name (str): The name of the prompt to update.
1405            version (int): The version number of the prompt to update.
1406            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 [].
1407
1408        Returns:
1409            Prompt: The updated prompt from the Langfuse API.
1410
1411        """
1412        updated_prompt = self.client.prompt_version.update(
1413            name=name,
1414            version=version,
1415            new_labels=new_labels,
1416        )
1417        self.prompt_cache.invalidate(name)
1418        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 trace( self, *, id: Optional[str] = None, 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, timestamp: Optional[datetime.datetime] = None, public: Optional[bool] = None, **kwargs) -> StatefulTraceClient:
1423    def trace(
1424        self,
1425        *,
1426        id: typing.Optional[str] = None,
1427        name: typing.Optional[str] = None,
1428        user_id: typing.Optional[str] = None,
1429        session_id: typing.Optional[str] = None,
1430        version: typing.Optional[str] = None,
1431        input: typing.Optional[typing.Any] = None,
1432        output: typing.Optional[typing.Any] = None,
1433        metadata: typing.Optional[typing.Any] = None,
1434        tags: typing.Optional[typing.List[str]] = None,
1435        timestamp: typing.Optional[dt.datetime] = None,
1436        public: typing.Optional[bool] = None,
1437        **kwargs,
1438    ) -> "StatefulTraceClient":
1439        """Create a trace.
1440
1441        Args:
1442            id: The id of the trace can be set, defaults to a random id. Set it to link traces to external systems or when creating a distributed trace. Traces are upserted on id.
1443            name: Identifier of the trace. Useful for sorting/filtering in the UI.
1444            input: The input of the trace. Can be any JSON object.
1445            output: The output of the trace. Can be any JSON object.
1446            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
1447            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
1448            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
1449            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
1450            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
1451            tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
1452            timestamp: The timestamp of the trace. Defaults to the current time if not provided.
1453            public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project.
1454            **kwargs: Additional keyword arguments that can be included in the trace.
1455
1456        Returns:
1457            StatefulTraceClient: The created trace.
1458
1459        Example:
1460            ```python
1461            from langfuse import Langfuse
1462
1463            langfuse = Langfuse()
1464
1465            trace = langfuse.trace(
1466                name="example-application",
1467                user_id="user-1234")
1468            )
1469            ```
1470        """
1471        new_id = id or str(uuid.uuid4())
1472        self.trace_id = new_id
1473        try:
1474            new_dict = {
1475                "id": new_id,
1476                "name": name,
1477                "userId": user_id,
1478                "sessionId": session_id
1479                or kwargs.get("sessionId", None),  # backward compatibility
1480                "release": self.release,
1481                "version": version,
1482                "metadata": metadata,
1483                "input": input,
1484                "output": output,
1485                "tags": tags,
1486                "timestamp": timestamp or _get_timestamp(),
1487                "public": public,
1488                "environment": self.environment,
1489            }
1490            if kwargs is not None:
1491                new_dict.update(kwargs)
1492
1493            new_body = TraceBody(**new_dict)
1494
1495            self.log.debug(f"Creating trace {_filter_io_from_event_body(new_dict)}")
1496            event = {
1497                "id": str(uuid.uuid4()),
1498                "type": "trace-create",
1499                "body": new_body,
1500            }
1501
1502            self.task_manager.add_task(
1503                event,
1504            )
1505
1506        except Exception as e:
1507            self.log.exception(e)
1508        finally:
1509            self._log_memory_usage()
1510
1511            return StatefulTraceClient(
1512                self.client,
1513                new_id,
1514                StateType.TRACE,
1515                new_id,
1516                self.task_manager,
1517                self.environment,
1518            )

Create a trace.

Arguments:
  • id: The id of the trace can be set, defaults to a random id. Set it to link traces to external systems or when creating a distributed trace. Traces are upserted on id.
  • name: Identifier of the trace. Useful for sorting/filtering in the UI.
  • input: The input of the trace. Can be any JSON object.
  • output: The output of the trace. Can be any JSON object.
  • metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
  • user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
  • session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
  • version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
  • release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
  • tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
  • timestamp: The timestamp of the trace. Defaults to the current time if not provided.
  • public: You can make a trace public to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project.
  • **kwargs: Additional keyword arguments that can be included in the trace.
Returns:

StatefulTraceClient: The created trace.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

trace = langfuse.trace(
    name="example-application",
    user_id="user-1234")
)
def score( self, *, name: str, value: Union[float, str], data_type: Optional[Literal['NUMERIC', 'CATEGORICAL', 'BOOLEAN']] = None, trace_id: Optional[str] = None, id: Optional[str] = None, comment: Optional[str] = None, observation_id: Optional[str] = None, config_id: Optional[str] = None, **kwargs) -> StatefulClient:
1597    def score(
1598        self,
1599        *,
1600        name: str,
1601        value: typing.Union[float, str],
1602        data_type: typing.Optional[ScoreDataType] = None,
1603        trace_id: typing.Optional[str] = None,
1604        id: typing.Optional[str] = None,
1605        comment: typing.Optional[str] = None,
1606        observation_id: typing.Optional[str] = None,
1607        config_id: typing.Optional[str] = None,
1608        **kwargs,
1609    ) -> "StatefulClient":
1610        """Create a score attached to a trace (and optionally an observation).
1611
1612        Args:
1613            name (str): Identifier of the score.
1614            value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
1615            data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present.
1616              When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
1617            trace_id (str): The id of the trace to which the score should be attached.
1618            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
1619            comment (Optional[str]): Additional context/explanation of the score.
1620            observation_id (Optional[str]): The id of the observation to which the score should be attached.
1621            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
1622            **kwargs: Additional keyword arguments to include in the score.
1623
1624        Returns:
1625            StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided).
1626
1627        Example:
1628            ```python
1629            from langfuse import Langfuse
1630
1631            langfuse = Langfuse()
1632
1633            # Create a trace
1634            trace = langfuse.trace(name="example-application")
1635
1636            # Get id of created trace
1637            trace_id = trace.id
1638
1639            # Add score to the trace
1640            trace = langfuse.score(
1641                trace_id=trace_id,
1642                name="user-explicit-feedback",
1643                value=0.9,
1644                comment="I like how personalized the response is"
1645            )
1646            ```
1647        """
1648        trace_id = trace_id or self.trace_id or str(uuid.uuid4())
1649        new_id = id or str(uuid.uuid4())
1650        try:
1651            new_dict = {
1652                "id": new_id,
1653                "trace_id": trace_id,
1654                "observation_id": observation_id,
1655                "name": name,
1656                "value": value,
1657                "data_type": data_type,
1658                "comment": comment,
1659                "config_id": config_id,
1660                "environment": self.environment,
1661                **kwargs,
1662            }
1663
1664            self.log.debug(f"Creating score {new_dict}...")
1665            new_body = ScoreBody(**new_dict)
1666
1667            event = {
1668                "id": str(uuid.uuid4()),
1669                "type": "score-create",
1670                "body": new_body,
1671            }
1672            self.task_manager.add_task(event)
1673
1674        except Exception as e:
1675            self.log.exception(e)
1676        finally:
1677            if observation_id is not None:
1678                return StatefulClient(
1679                    self.client,
1680                    observation_id,
1681                    StateType.OBSERVATION,
1682                    trace_id,
1683                    self.task_manager,
1684                    self.environment,
1685                )
1686            else:
1687                return StatefulClient(
1688                    self.client,
1689                    new_id,
1690                    StateType.TRACE,
1691                    new_id,
1692                    self.task_manager,
1693                    self.environment,
1694                )

Create a score attached to a trace (and optionally an observation).

Arguments:
  • name (str): Identifier of the score.
  • value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
  • data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
  • trace_id (str): The id of the trace to which the score should be attached.
  • id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
  • comment (Optional[str]): Additional context/explanation of the score.
  • observation_id (Optional[str]): The id of the observation to which the score should be attached.
  • config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
  • **kwargs: Additional keyword arguments to include in the score.
Returns:

StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided).

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name="example-application")

# Get id of created trace
trace_id = trace.id

# Add score to the trace
trace = langfuse.score(
    trace_id=trace_id,
    name="user-explicit-feedback",
    value=0.9,
    comment="I like how personalized the response is"
)
def span( self, *, id: Optional[str] = None, trace_id: Optional[str] = None, parent_observation_id: Optional[str] = None, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, input: Optional[Any] = None, output: Optional[Any] = None, version: Optional[str] = None, **kwargs) -> StatefulSpanClient:
1696    def span(
1697        self,
1698        *,
1699        id: typing.Optional[str] = None,
1700        trace_id: typing.Optional[str] = None,
1701        parent_observation_id: typing.Optional[str] = None,
1702        name: typing.Optional[str] = None,
1703        start_time: typing.Optional[dt.datetime] = None,
1704        end_time: typing.Optional[dt.datetime] = None,
1705        metadata: typing.Optional[typing.Any] = None,
1706        level: typing.Optional[SpanLevel] = None,
1707        status_message: typing.Optional[str] = None,
1708        input: typing.Optional[typing.Any] = None,
1709        output: typing.Optional[typing.Any] = None,
1710        version: typing.Optional[str] = None,
1711        **kwargs,
1712    ) -> "StatefulSpanClient":
1713        """Create a span.
1714
1715        A span represents durations of units of work in a trace.
1716        Usually, you want to add a span nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
1717
1718        If no trace_id is provided, a new trace is created just for this span.
1719
1720        Args:
1721            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
1722            trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated.
1723            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1724            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
1725            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
1726            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
1727            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
1728            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
1729            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
1730            input (Optional[dict]): The input to the span. Can be any JSON object.
1731            output (Optional[dict]): The output to the span. Can be any JSON object.
1732            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1733            **kwargs: Additional keyword arguments to include in the span.
1734
1735        Returns:
1736            StatefulSpanClient: The created span.
1737
1738        Example:
1739            ```python
1740            from langfuse import Langfuse
1741
1742            langfuse = Langfuse()
1743
1744            trace = langfuse.trace(name = "llm-feature")
1745
1746            # Create a span
1747            retrieval = langfuse.span(name = "retrieval", trace_id = trace.id)
1748
1749            # Create a nested span
1750            nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id)
1751            ```
1752        """
1753        new_span_id = id or str(uuid.uuid4())
1754        new_trace_id = trace_id or str(uuid.uuid4())
1755        self.trace_id = new_trace_id
1756        try:
1757            span_body = {
1758                "id": new_span_id,
1759                "trace_id": new_trace_id,
1760                "name": name,
1761                "start_time": start_time or _get_timestamp(),
1762                "metadata": metadata,
1763                "input": input,
1764                "output": output,
1765                "level": level,
1766                "status_message": status_message,
1767                "parent_observation_id": parent_observation_id,
1768                "version": version,
1769                "end_time": end_time,
1770                "trace": {"release": self.release},
1771                "environment": self.environment,
1772                **kwargs,
1773            }
1774
1775            if trace_id is None:
1776                self._generate_trace(new_trace_id, name or new_trace_id)
1777
1778            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
1779
1780            span_body = CreateSpanBody(**span_body)
1781
1782            event = {
1783                "id": str(uuid.uuid4()),
1784                "type": "span-create",
1785                "body": span_body,
1786            }
1787
1788            self.task_manager.add_task(event)
1789
1790        except Exception as e:
1791            self.log.exception(e)
1792        finally:
1793            self._log_memory_usage()
1794
1795            return StatefulSpanClient(
1796                self.client,
1797                new_span_id,
1798                StateType.OBSERVATION,
1799                new_trace_id,
1800                self.task_manager,
1801                self.environment,
1802            )

Create a span.

A span represents durations of units of work in a trace. Usually, you want to add a span nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.

If no trace_id is provided, a new trace is created just for this span.

Arguments:
  • id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
  • trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated.
  • parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
  • name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
  • end_time (Optional[datetime]): The time at which the span ended. Automatically set by span.end().
  • metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
  • level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
  • input (Optional[dict]): The input to the span. Can be any JSON object.
  • output (Optional[dict]): The output to the span. Can be any JSON object.
  • version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
  • **kwargs: Additional keyword arguments to include in the span.
Returns:

StatefulSpanClient: The created span.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

trace = langfuse.trace(name = "llm-feature")

# Create a span
retrieval = langfuse.span(name = "retrieval", trace_id = trace.id)

# Create a nested span
nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id)
def event( self, *, id: Optional[str] = None, trace_id: Optional[str] = None, parent_observation_id: Optional[str] = None, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, input: Optional[Any] = None, output: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, **kwargs) -> StatefulSpanClient:
1804    def event(
1805        self,
1806        *,
1807        id: typing.Optional[str] = None,
1808        trace_id: typing.Optional[str] = None,
1809        parent_observation_id: typing.Optional[str] = None,
1810        name: typing.Optional[str] = None,
1811        start_time: typing.Optional[dt.datetime] = None,
1812        metadata: typing.Optional[typing.Any] = None,
1813        input: typing.Optional[typing.Any] = None,
1814        output: typing.Optional[typing.Any] = None,
1815        level: typing.Optional[SpanLevel] = None,
1816        status_message: typing.Optional[str] = None,
1817        version: typing.Optional[str] = None,
1818        **kwargs,
1819    ) -> "StatefulSpanClient":
1820        """Create an event.
1821
1822        An event represents a discrete event in a trace.
1823        Usually, you want to add a event nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
1824
1825        If no trace_id is provided, a new trace is created just for this event.
1826
1827        Args:
1828            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
1829            trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event.
1830            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1831            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
1832            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
1833            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
1834            input (Optional[Any]): The input to the event. Can be any JSON object.
1835            output (Optional[Any]): The output to the event. Can be any JSON object.
1836            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
1837            status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event.
1838            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
1839            **kwargs: Additional keyword arguments to include in the event.
1840
1841        Returns:
1842            StatefulSpanClient: The created event.
1843
1844        Example:
1845            ```python
1846            from langfuse import Langfuse
1847
1848            langfuse = Langfuse()
1849
1850            trace = langfuse.trace(name = "llm-feature")
1851
1852            # Create an event
1853            retrieval = langfuse.event(name = "retrieval", trace_id = trace.id)
1854            ```
1855        """
1856        event_id = id or str(uuid.uuid4())
1857        new_trace_id = trace_id or str(uuid.uuid4())
1858        self.trace_id = new_trace_id
1859        try:
1860            event_body = {
1861                "id": event_id,
1862                "trace_id": new_trace_id,
1863                "name": name,
1864                "start_time": start_time or _get_timestamp(),
1865                "metadata": metadata,
1866                "input": input,
1867                "output": output,
1868                "level": level,
1869                "status_message": status_message,
1870                "parent_observation_id": parent_observation_id,
1871                "version": version,
1872                "trace": {"release": self.release},
1873                "environment": self.environment,
1874                **kwargs,
1875            }
1876
1877            if trace_id is None:
1878                self._generate_trace(new_trace_id, name or new_trace_id)
1879
1880            request = CreateEventBody(**event_body)
1881
1882            event = {
1883                "id": str(uuid.uuid4()),
1884                "type": "event-create",
1885                "body": request,
1886            }
1887
1888            self.log.debug(
1889                f"Creating event {_filter_io_from_event_body(event_body)} ..."
1890            )
1891            self.task_manager.add_task(event)
1892
1893        except Exception as e:
1894            self.log.exception(e)
1895        finally:
1896            return StatefulSpanClient(
1897                self.client,
1898                event_id,
1899                StateType.OBSERVATION,
1900                new_trace_id,
1901                self.task_manager,
1902                self.environment,
1903            )

Create an event.

An event represents a discrete event in a trace. Usually, you want to add a event nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.

If no trace_id is provided, a new trace is created just for this event.

Arguments:
  • id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
  • trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event.
  • parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
  • name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
  • metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
  • input (Optional[Any]): The input to the event. Can be any JSON object.
  • output (Optional[Any]): The output to the event. Can be any JSON object.
  • level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event.
  • version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
  • **kwargs: Additional keyword arguments to include in the event.
Returns:

StatefulSpanClient: The created event.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

trace = langfuse.trace(name = "llm-feature")

# Create an event
retrieval = langfuse.event(name = "retrieval", trace_id = trace.id)
def generation( self, *, id: Optional[str] = None, trace_id: Optional[str] = None, parent_observation_id: Optional[str] = None, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, completion_start_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, input: Optional[Any] = None, output: Optional[Any] = None, usage: Union[pydantic.v1.main.BaseModel, langfuse.model.ModelUsage, NoneType] = 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) -> StatefulGenerationClient:
1905    def generation(
1906        self,
1907        *,
1908        id: typing.Optional[str] = None,
1909        trace_id: typing.Optional[str] = None,
1910        parent_observation_id: typing.Optional[str] = None,
1911        name: typing.Optional[str] = None,
1912        start_time: typing.Optional[dt.datetime] = None,
1913        end_time: typing.Optional[dt.datetime] = None,
1914        completion_start_time: typing.Optional[dt.datetime] = None,
1915        metadata: typing.Optional[typing.Any] = None,
1916        level: typing.Optional[SpanLevel] = None,
1917        status_message: typing.Optional[str] = None,
1918        version: typing.Optional[str] = None,
1919        model: typing.Optional[str] = None,
1920        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
1921        input: typing.Optional[typing.Any] = None,
1922        output: typing.Optional[typing.Any] = None,
1923        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
1924        usage_details: typing.Optional[typing.Dict[str, int]] = None,
1925        cost_details: typing.Optional[typing.Dict[str, float]] = None,
1926        prompt: typing.Optional[PromptClient] = None,
1927        **kwargs,
1928    ) -> "StatefulGenerationClient":
1929        """Create a generation.
1930
1931        A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.
1932
1933        Usually, you want to add a generation nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
1934
1935        If no trace_id is provided, a new trace is created just for this generation.
1936
1937        Args:
1938            id (Optional[str]): The id of the generation can be set, defaults to random id.
1939            trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created
1940            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1941            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
1942            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
1943            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
1944            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
1945            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
1946            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
1947            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
1948            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1949            model (Optional[str]): The name of the model used for the generation.
1950            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
1951            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
1952            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
1953            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
1954            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
1955            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
1956            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
1957            **kwargs: Additional keyword arguments to include in the generation.
1958
1959        Returns:
1960            StatefulGenerationClient: The created generation.
1961
1962        Example:
1963            ```python
1964            from langfuse import Langfuse
1965
1966            langfuse = Langfuse()
1967
1968            # Create a generation in Langfuse
1969            generation = langfuse.generation(
1970                name="summary-generation",
1971                model="gpt-3.5-turbo",
1972                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
1973                input=[{"role": "system", "content": "You are a helpful assistant."},
1974                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
1975                metadata={"interface": "whatsapp"}
1976            )
1977            ```
1978        """
1979        new_trace_id = trace_id or str(uuid.uuid4())
1980        new_generation_id = id or str(uuid.uuid4())
1981        self.trace_id = new_trace_id
1982        try:
1983            generation_body = {
1984                "id": new_generation_id,
1985                "trace_id": new_trace_id,
1986                "release": self.release,
1987                "name": name,
1988                "start_time": start_time or _get_timestamp(),
1989                "metadata": metadata,
1990                "input": input,
1991                "output": output,
1992                "level": level,
1993                "status_message": status_message,
1994                "parent_observation_id": parent_observation_id,
1995                "version": version,
1996                "end_time": end_time,
1997                "completion_start_time": completion_start_time,
1998                "model": model,
1999                "model_parameters": model_parameters,
2000                "usage": _convert_usage_input(usage) if usage is not None else None,
2001                "usage_details": usage_details,
2002                "cost_details": cost_details,
2003                "trace": {"release": self.release},
2004                "environment": self.environment,
2005                **_create_prompt_context(prompt),
2006                **kwargs,
2007            }
2008
2009            if trace_id is None:
2010                trace = {
2011                    "id": new_trace_id,
2012                    "release": self.release,
2013                    "name": name,
2014                    "environment": self.environment,
2015                }
2016                request = TraceBody(**trace)
2017
2018                event = {
2019                    "id": str(uuid.uuid4()),
2020                    "type": "trace-create",
2021                    "body": request,
2022                }
2023
2024                self.log.debug("Creating trace...")
2025
2026                self.task_manager.add_task(event)
2027
2028            self.log.debug(
2029                f"Creating generation max {_filter_io_from_event_body(generation_body)}..."
2030            )
2031            request = CreateGenerationBody(**generation_body)
2032
2033            event = {
2034                "id": str(uuid.uuid4()),
2035                "type": "generation-create",
2036                "body": request,
2037            }
2038
2039            self.task_manager.add_task(event)
2040
2041        except Exception as e:
2042            self.log.exception(e)
2043        finally:
2044            return StatefulGenerationClient(
2045                self.client,
2046                new_generation_id,
2047                StateType.OBSERVATION,
2048                new_trace_id,
2049                self.task_manager,
2050                self.environment,
2051            )

Create a generation.

A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.

Usually, you want to add a generation nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.

If no trace_id is provided, a new trace is created just for this generation.

Arguments:
  • id (Optional[str]): The id of the generation can be set, defaults to random id.
  • trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created
  • parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
  • name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
  • end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by generation.end().
  • completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
  • metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
  • level (Optional[str]): The level of the generation. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
  • version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
  • model (Optional[str]): The name of the model used for the generation.
  • model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
  • input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
  • output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
  • usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {promptTokens, completionTokens, totalTokens} and a more generic version {input, output, total, unit, inputCost, outputCost, totalCost} where unit can be of value "TOKENS", "CHARACTERS", "MILLISECONDS", "SECONDS", or "IMAGES". Refer to the docs on how to automatically infer token usage and costs in Langfuse.
  • usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
  • cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
  • prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
  • **kwargs: Additional keyword arguments to include in the generation.
Returns:

StatefulGenerationClient: The created generation.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a generation in Langfuse
generation = langfuse.generation(
    name="summary-generation",
    model="gpt-3.5-turbo",
    model_parameters={"maxTokens": "1000", "temperature": "0.9"},
    input=[{"role": "system", "content": "You are a helpful assistant."},
           {"role": "user", "content": "Please generate a summary of the following documents ..."}],
    metadata={"interface": "whatsapp"}
)
def join(self):
2072    def join(self):
2073        """Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter.
2074
2075        If called before flushing, consumers might terminate before sending all events to Langfuse API. This method is called at exit of the SKD, right before the Python interpreter closes.
2076        To guarantee all messages have been delivered, you still need to call flush().
2077        """
2078        try:
2079            return self.task_manager.join()
2080        except Exception as e:
2081            self.log.exception(e)

Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter.

If called before flushing, consumers might terminate before sending all events to Langfuse API. This method is called at exit of the SKD, right before the Python interpreter closes. To guarantee all messages have been delivered, you still need to call flush().

def flush(self):
2083    def flush(self):
2084        """Flush the internal event queue to the Langfuse API. It blocks until the queue is empty. It should be called when the application shuts down.
2085
2086        Example:
2087            ```python
2088            from langfuse import Langfuse
2089
2090            langfuse = Langfuse()
2091
2092            # Some operations with Langfuse
2093
2094            # Flushing all events to end Langfuse cleanly
2095            langfuse.flush()
2096            ```
2097        """
2098        try:
2099            return self.task_manager.flush()
2100        except Exception as e:
2101            self.log.exception(e)

Flush the internal event queue to the Langfuse API. It blocks until the queue is empty. It should be called when the application shuts down.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Some operations with Langfuse

# Flushing all events to end Langfuse cleanly
langfuse.flush()
def shutdown(self):
2103    def shutdown(self):
2104        """Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated.
2105
2106        This function calls flush() and join() consecutively resulting in a complete shutdown of the SDK. On success of this function, no more events will be sent to Langfuse API.
2107        As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API.
2108        """
2109        try:
2110            self.prompt_cache._task_manager.shutdown()
2111
2112            # In logging.py, a handler is attached to the httpx logger.
2113            # To avoid a memory leak on singleton reset, remove all handlers
2114            httpx_logger = logging.getLogger("httpx")
2115            for handler in httpx_logger.handlers:
2116                httpx_logger.removeHandler(handler)
2117
2118            return self.task_manager.shutdown()
2119        except Exception as e:
2120            self.log.exception(e)

Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated.

This function calls flush() and join() consecutively resulting in a complete shutdown of the SDK. On success of this function, no more events will be sent to Langfuse API. As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API.

class StateType(enum.Enum):
2123class StateType(Enum):
2124    """Enum to distinguish observation and trace states.
2125
2126    Attributes:
2127        OBSERVATION (int): Observation state.
2128        TRACE (int): Trace state.
2129    """
2130
2131    OBSERVATION = 1
2132    TRACE = 0

Enum to distinguish observation and trace states.

Attributes:
  • OBSERVATION (int): Observation state.
  • TRACE (int): Trace state.
OBSERVATION = <StateType.OBSERVATION: 1>
TRACE = <StateType.TRACE: 0>
Inherited Members
enum.Enum
name
value
class StatefulClient:
2135class StatefulClient(object):
2136    """Base class for handling stateful operations in the Langfuse system.
2137
2138    This client is capable of creating different nested Langfuse objects like spans, generations, scores, and events,
2139    associating them with either an observation or a trace based on the specified state type.
2140
2141    Attributes:
2142        client (FernLangfuse): Core interface for Langfuse API interactions.
2143        id (str): Unique identifier of the stateful client (either observation or trace).
2144        state_type (StateType): Enum indicating whether the client is an observation or a trace.
2145        trace_id (str): Id of the trace associated with the stateful client.
2146        task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2147        environment (Optional(str)): The tracing environment.
2148    """
2149
2150    log = logging.getLogger("langfuse")
2151
2152    def __init__(
2153        self,
2154        client: FernLangfuse,
2155        id: str,
2156        state_type: StateType,
2157        trace_id: str,
2158        task_manager: TaskManager,
2159        environment: Optional[str] = None,
2160    ):
2161        """Initialize the StatefulClient.
2162
2163        Args:
2164            client (FernLangfuse): Core interface for Langfuse API interactions.
2165            id (str): Unique identifier of the stateful client (either observation or trace).
2166            state_type (StateType): Enum indicating whether the client is an observation or a trace.
2167            trace_id (str): Id of the trace associated with the stateful client.
2168            task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2169        """
2170        self.client = client
2171        self.trace_id = trace_id
2172        self.id = id
2173        self.state_type = state_type
2174        self.task_manager = task_manager
2175
2176        self.environment = environment or os.environ.get("LANGFUSE_TRACING_ENVIRONMENT")
2177
2178        if self.environment and not bool(
2179            re.match(ENVIRONMENT_PATTERN, self.environment)
2180        ):
2181            self.log.warning(
2182                f'Invalid environment specified "{environment}" that does not match validation pattern ("{ENVIRONMENT_PATTERN}"). Setting will be ignored.'
2183            )
2184
2185    def _add_state_to_event(self, body: dict):
2186        if self.state_type == StateType.OBSERVATION:
2187            body["parent_observation_id"] = self.id
2188            body["trace_id"] = self.trace_id
2189        else:
2190            body["trace_id"] = self.id
2191        return body
2192
2193    def _add_default_values(self, body: dict):
2194        if body.get("start_time") is None:
2195            body["start_time"] = _get_timestamp()
2196        return body
2197
2198    def generation(
2199        self,
2200        *,
2201        id: typing.Optional[str] = None,
2202        name: typing.Optional[str] = None,
2203        start_time: typing.Optional[dt.datetime] = None,
2204        end_time: typing.Optional[dt.datetime] = None,
2205        metadata: typing.Optional[typing.Any] = None,
2206        level: typing.Optional[SpanLevel] = None,
2207        status_message: typing.Optional[str] = None,
2208        version: typing.Optional[str] = None,
2209        completion_start_time: typing.Optional[dt.datetime] = None,
2210        model: typing.Optional[str] = None,
2211        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2212        input: typing.Optional[typing.Any] = None,
2213        output: typing.Optional[typing.Any] = None,
2214        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2215        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2216        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2217        prompt: typing.Optional[PromptClient] = None,
2218        **kwargs,
2219    ) -> "StatefulGenerationClient":
2220        """Create a generation nested within the current observation or trace.
2221
2222        A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.
2223
2224        Args:
2225            id (Optional[str]): The id of the generation can be set, defaults to random id.
2226            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2227            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2228            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2229            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
2230            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2231            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2232            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
2233            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2234            model (Optional[str]): The name of the model used for the generation.
2235            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2236            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2237            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2238            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
2239            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
2240            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
2241            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2242            **kwargs: Additional keyword arguments to include in the generation.
2243
2244        Returns:
2245            StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.
2246
2247        Example:
2248            ```python
2249            from langfuse import Langfuse
2250
2251            langfuse = Langfuse()
2252
2253            # Create a trace
2254            trace = langfuse.trace(name = "llm-feature")
2255
2256            # Create a nested generation in Langfuse
2257            generation = trace.generation(
2258                name="summary-generation",
2259                model="gpt-3.5-turbo",
2260                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
2261                input=[{"role": "system", "content": "You are a helpful assistant."},
2262                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
2263                metadata={"interface": "whatsapp"}
2264            )
2265            ```
2266        """
2267        generation_id = id or str(uuid.uuid4())
2268        try:
2269            generation_body = {
2270                "id": generation_id,
2271                "name": name,
2272                "start_time": start_time or _get_timestamp(),
2273                "metadata": metadata,
2274                "level": level,
2275                "status_message": status_message,
2276                "version": version,
2277                "end_time": end_time,
2278                "completion_start_time": completion_start_time,
2279                "model": model,
2280                "model_parameters": model_parameters,
2281                "input": input,
2282                "output": output,
2283                "usage": _convert_usage_input(usage) if usage is not None else None,
2284                "usage_details": usage_details,
2285                "cost_details": cost_details,
2286                "environment": self.environment,
2287                **_create_prompt_context(prompt),
2288                **kwargs,
2289            }
2290
2291            generation_body = self._add_state_to_event(generation_body)
2292            new_body = self._add_default_values(generation_body)
2293
2294            new_body = CreateGenerationBody(**new_body)
2295
2296            event = {
2297                "id": str(uuid.uuid4()),
2298                "type": "generation-create",
2299                "body": new_body.dict(exclude_none=True, exclude_unset=False),
2300            }
2301
2302            self.log.debug(
2303                f"Creating generation {_filter_io_from_event_body(generation_body)}..."
2304            )
2305            self.task_manager.add_task(event)
2306
2307        except Exception as e:
2308            self.log.exception(e)
2309        finally:
2310            return StatefulGenerationClient(
2311                self.client,
2312                generation_id,
2313                StateType.OBSERVATION,
2314                self.trace_id,
2315                self.task_manager,
2316                self.environment,
2317            )
2318
2319    def span(
2320        self,
2321        *,
2322        id: typing.Optional[str] = None,
2323        name: typing.Optional[str] = None,
2324        start_time: typing.Optional[dt.datetime] = None,
2325        end_time: typing.Optional[dt.datetime] = None,
2326        metadata: typing.Optional[typing.Any] = None,
2327        input: typing.Optional[typing.Any] = None,
2328        output: typing.Optional[typing.Any] = None,
2329        level: typing.Optional[SpanLevel] = None,
2330        status_message: typing.Optional[str] = None,
2331        version: typing.Optional[str] = None,
2332        **kwargs,
2333    ) -> "StatefulSpanClient":
2334        """Create a span nested within the current observation or trace.
2335
2336        A span represents durations of units of work in a trace.
2337
2338        Args:
2339            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
2340            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2341            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2342            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2343            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2344            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2345            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
2346            input (Optional[dict]): The input to the span. Can be any JSON object.
2347            output (Optional[dict]): The output to the span. Can be any JSON object.
2348            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2349            **kwargs: Additional keyword arguments to include in the span.
2350
2351        Returns:
2352            StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.
2353
2354        Example:
2355            ```python
2356            from langfuse import Langfuse
2357
2358            langfuse = Langfuse()
2359
2360            # Create a trace
2361            trace = langfuse.trace(name = "llm-feature")
2362
2363            # Create a span
2364            retrieval = langfuse.span(name = "retrieval")
2365            ```
2366        """
2367        span_id = id or str(uuid.uuid4())
2368        try:
2369            span_body = {
2370                "id": span_id,
2371                "name": name,
2372                "start_time": start_time or _get_timestamp(),
2373                "metadata": metadata,
2374                "input": input,
2375                "output": output,
2376                "level": level,
2377                "status_message": status_message,
2378                "version": version,
2379                "end_time": end_time,
2380                "environment": self.environment,
2381                **kwargs,
2382            }
2383
2384            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
2385
2386            new_dict = self._add_state_to_event(span_body)
2387            new_body = self._add_default_values(new_dict)
2388
2389            event = CreateSpanBody(**new_body)
2390
2391            event = {
2392                "id": str(uuid.uuid4()),
2393                "type": "span-create",
2394                "body": event,
2395            }
2396
2397            self.task_manager.add_task(event)
2398        except Exception as e:
2399            self.log.exception(e)
2400        finally:
2401            return StatefulSpanClient(
2402                self.client,
2403                span_id,
2404                StateType.OBSERVATION,
2405                self.trace_id,
2406                self.task_manager,
2407                self.environment,
2408            )
2409
2410    @overload
2411    def score(
2412        self,
2413        *,
2414        id: typing.Optional[str] = None,
2415        name: str,
2416        value: float,
2417        data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None,
2418        comment: typing.Optional[str] = None,
2419        config_id: typing.Optional[str] = None,
2420        **kwargs,
2421    ) -> "StatefulClient": ...
2422
2423    @overload
2424    def score(
2425        self,
2426        *,
2427        id: typing.Optional[str] = None,
2428        name: str,
2429        value: str,
2430        data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL",
2431        comment: typing.Optional[str] = None,
2432        config_id: typing.Optional[str] = None,
2433        **kwargs,
2434    ) -> "StatefulClient": ...
2435
2436    def score(
2437        self,
2438        *,
2439        id: typing.Optional[str] = None,
2440        name: str,
2441        value: typing.Union[float, str],
2442        data_type: typing.Optional[ScoreDataType] = None,
2443        comment: typing.Optional[str] = None,
2444        config_id: typing.Optional[str] = None,
2445        **kwargs,
2446    ) -> "StatefulClient":
2447        """Create a score attached for the current observation or trace.
2448
2449        Args:
2450            name (str): Identifier of the score.
2451            value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
2452            data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present.
2453              When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
2454            comment (Optional[str]): Additional context/explanation of the score.
2455            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
2456            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
2457            **kwargs: Additional keyword arguments to include in the score.
2458
2459        Returns:
2460            StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.
2461
2462        Example:
2463            ```python
2464            from langfuse import Langfuse
2465
2466            langfuse = Langfuse()
2467
2468            # Create a trace
2469            trace = langfuse.trace(name="example-application")
2470
2471            # Add score to the trace
2472            trace = trace.score(
2473                name="user-explicit-feedback",
2474                value=0.8,
2475                comment="I like how personalized the response is"
2476            )
2477            ```
2478        """
2479        score_id = id or str(uuid.uuid4())
2480        try:
2481            new_score = {
2482                "id": score_id,
2483                "trace_id": self.trace_id,
2484                "name": name,
2485                "value": value,
2486                "data_type": data_type,
2487                "comment": comment,
2488                "config_id": config_id,
2489                "environment": self.environment,
2490                **kwargs,
2491            }
2492
2493            self.log.debug(f"Creating score {new_score}...")
2494
2495            new_dict = self._add_state_to_event(new_score)
2496
2497            if self.state_type == StateType.OBSERVATION:
2498                new_dict["observationId"] = self.id
2499
2500            request = ScoreBody(**new_dict)
2501
2502            event = {
2503                "id": str(uuid.uuid4()),
2504                "type": "score-create",
2505                "body": request,
2506            }
2507
2508            self.task_manager.add_task(event)
2509
2510        except Exception as e:
2511            self.log.exception(e)
2512        finally:
2513            return StatefulClient(
2514                self.client,
2515                self.id,
2516                self.state_type,
2517                self.trace_id,
2518                self.task_manager,
2519                self.environment,
2520            )
2521
2522    def event(
2523        self,
2524        *,
2525        id: typing.Optional[str] = None,
2526        name: typing.Optional[str] = None,
2527        start_time: typing.Optional[dt.datetime] = None,
2528        metadata: typing.Optional[typing.Any] = None,
2529        input: typing.Optional[typing.Any] = None,
2530        output: typing.Optional[typing.Any] = None,
2531        level: typing.Optional[SpanLevel] = None,
2532        status_message: typing.Optional[str] = None,
2533        version: typing.Optional[str] = None,
2534        **kwargs,
2535    ) -> "StatefulClient":
2536        """Create an event nested within the current observation or trace.
2537
2538        An event represents a discrete event in a trace.
2539
2540        Args:
2541            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
2542            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
2543            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
2544            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
2545            input (Optional[Any]): The input to the event. Can be any JSON object.
2546            output (Optional[Any]): The output to the event. Can be any JSON object.
2547            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2548            status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event.
2549            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
2550            **kwargs: Additional keyword arguments to include in the event.
2551
2552        Returns:
2553            StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations.
2554
2555        Example:
2556            ```python
2557            from langfuse import Langfuse
2558
2559            langfuse = Langfuse()
2560
2561            # Create a trace
2562            trace = langfuse.trace(name = "llm-feature")
2563
2564            # Create an event
2565            retrieval = trace.event(name = "retrieval")
2566            ```
2567        """
2568        event_id = id or str(uuid.uuid4())
2569        try:
2570            event_body = {
2571                "id": event_id,
2572                "name": name,
2573                "start_time": start_time or _get_timestamp(),
2574                "metadata": metadata,
2575                "input": input,
2576                "output": output,
2577                "level": level,
2578                "status_message": status_message,
2579                "version": version,
2580                "environment": self.environment,
2581                **kwargs,
2582            }
2583
2584            new_dict = self._add_state_to_event(event_body)
2585            new_body = self._add_default_values(new_dict)
2586
2587            request = CreateEventBody(**new_body)
2588
2589            event = {
2590                "id": str(uuid.uuid4()),
2591                "type": "event-create",
2592                "body": request,
2593            }
2594
2595            self.log.debug(
2596                f"Creating event {_filter_io_from_event_body(event_body)}..."
2597            )
2598            self.task_manager.add_task(event)
2599
2600        except Exception as e:
2601            self.log.exception(e)
2602        finally:
2603            return StatefulClient(
2604                self.client,
2605                event_id,
2606                StateType.OBSERVATION,
2607                self.trace_id,
2608                self.task_manager,
2609                self.environment,
2610            )
2611
2612    def get_trace_url(self):
2613        """Get the URL to see the current trace in the Langfuse UI."""
2614        return f"{self.client._client_wrapper._base_url}/trace/{self.trace_id}"

Base class for handling stateful operations in the Langfuse system.

This client is capable of creating different nested Langfuse objects like spans, generations, scores, and events, associating them with either an observation or a trace based on the specified state type.

Attributes:
  • client (FernLangfuse): Core interface for Langfuse API interactions.
  • id (str): Unique identifier of the stateful client (either observation or trace).
  • state_type (StateType): Enum indicating whether the client is an observation or a trace.
  • trace_id (str): Id of the trace associated with the stateful client.
  • task_manager (TaskManager): Manager handling asynchronous tasks for the client.
  • environment (Optional(str)): The tracing environment.
StatefulClient( client: langfuse.api.client.FernLangfuse, id: str, state_type: StateType, trace_id: str, task_manager: langfuse._task_manager.task_manager.TaskManager, environment: Optional[str] = None)
2152    def __init__(
2153        self,
2154        client: FernLangfuse,
2155        id: str,
2156        state_type: StateType,
2157        trace_id: str,
2158        task_manager: TaskManager,
2159        environment: Optional[str] = None,
2160    ):
2161        """Initialize the StatefulClient.
2162
2163        Args:
2164            client (FernLangfuse): Core interface for Langfuse API interactions.
2165            id (str): Unique identifier of the stateful client (either observation or trace).
2166            state_type (StateType): Enum indicating whether the client is an observation or a trace.
2167            trace_id (str): Id of the trace associated with the stateful client.
2168            task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2169        """
2170        self.client = client
2171        self.trace_id = trace_id
2172        self.id = id
2173        self.state_type = state_type
2174        self.task_manager = task_manager
2175
2176        self.environment = environment or os.environ.get("LANGFUSE_TRACING_ENVIRONMENT")
2177
2178        if self.environment and not bool(
2179            re.match(ENVIRONMENT_PATTERN, self.environment)
2180        ):
2181            self.log.warning(
2182                f'Invalid environment specified "{environment}" that does not match validation pattern ("{ENVIRONMENT_PATTERN}"). Setting will be ignored.'
2183            )

Initialize the StatefulClient.

Arguments:
  • client (FernLangfuse): Core interface for Langfuse API interactions.
  • id (str): Unique identifier of the stateful client (either observation or trace).
  • state_type (StateType): Enum indicating whether the client is an observation or a trace.
  • trace_id (str): Id of the trace associated with the stateful client.
  • task_manager (TaskManager): Manager handling asynchronous tasks for the client.
log = <Logger langfuse (WARNING)>
client
trace_id
id
state_type
task_manager
environment
def generation( self, *, id: Optional[str] = None, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: 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, input: Optional[Any] = None, output: Optional[Any] = None, usage: Union[pydantic.v1.main.BaseModel, langfuse.model.ModelUsage, NoneType] = 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) -> StatefulGenerationClient:
2198    def generation(
2199        self,
2200        *,
2201        id: typing.Optional[str] = None,
2202        name: typing.Optional[str] = None,
2203        start_time: typing.Optional[dt.datetime] = None,
2204        end_time: typing.Optional[dt.datetime] = None,
2205        metadata: typing.Optional[typing.Any] = None,
2206        level: typing.Optional[SpanLevel] = None,
2207        status_message: typing.Optional[str] = None,
2208        version: typing.Optional[str] = None,
2209        completion_start_time: typing.Optional[dt.datetime] = None,
2210        model: typing.Optional[str] = None,
2211        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2212        input: typing.Optional[typing.Any] = None,
2213        output: typing.Optional[typing.Any] = None,
2214        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2215        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2216        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2217        prompt: typing.Optional[PromptClient] = None,
2218        **kwargs,
2219    ) -> "StatefulGenerationClient":
2220        """Create a generation nested within the current observation or trace.
2221
2222        A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.
2223
2224        Args:
2225            id (Optional[str]): The id of the generation can be set, defaults to random id.
2226            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2227            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2228            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2229            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
2230            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2231            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2232            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
2233            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2234            model (Optional[str]): The name of the model used for the generation.
2235            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2236            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2237            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2238            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
2239            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
2240            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
2241            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2242            **kwargs: Additional keyword arguments to include in the generation.
2243
2244        Returns:
2245            StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.
2246
2247        Example:
2248            ```python
2249            from langfuse import Langfuse
2250
2251            langfuse = Langfuse()
2252
2253            # Create a trace
2254            trace = langfuse.trace(name = "llm-feature")
2255
2256            # Create a nested generation in Langfuse
2257            generation = trace.generation(
2258                name="summary-generation",
2259                model="gpt-3.5-turbo",
2260                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
2261                input=[{"role": "system", "content": "You are a helpful assistant."},
2262                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
2263                metadata={"interface": "whatsapp"}
2264            )
2265            ```
2266        """
2267        generation_id = id or str(uuid.uuid4())
2268        try:
2269            generation_body = {
2270                "id": generation_id,
2271                "name": name,
2272                "start_time": start_time or _get_timestamp(),
2273                "metadata": metadata,
2274                "level": level,
2275                "status_message": status_message,
2276                "version": version,
2277                "end_time": end_time,
2278                "completion_start_time": completion_start_time,
2279                "model": model,
2280                "model_parameters": model_parameters,
2281                "input": input,
2282                "output": output,
2283                "usage": _convert_usage_input(usage) if usage is not None else None,
2284                "usage_details": usage_details,
2285                "cost_details": cost_details,
2286                "environment": self.environment,
2287                **_create_prompt_context(prompt),
2288                **kwargs,
2289            }
2290
2291            generation_body = self._add_state_to_event(generation_body)
2292            new_body = self._add_default_values(generation_body)
2293
2294            new_body = CreateGenerationBody(**new_body)
2295
2296            event = {
2297                "id": str(uuid.uuid4()),
2298                "type": "generation-create",
2299                "body": new_body.dict(exclude_none=True, exclude_unset=False),
2300            }
2301
2302            self.log.debug(
2303                f"Creating generation {_filter_io_from_event_body(generation_body)}..."
2304            )
2305            self.task_manager.add_task(event)
2306
2307        except Exception as e:
2308            self.log.exception(e)
2309        finally:
2310            return StatefulGenerationClient(
2311                self.client,
2312                generation_id,
2313                StateType.OBSERVATION,
2314                self.trace_id,
2315                self.task_manager,
2316                self.environment,
2317            )

Create a generation nested within the current observation or trace.

A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.

Arguments:
  • id (Optional[str]): The id of the generation can be set, defaults to random id.
  • name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
  • end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by generation.end().
  • completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
  • metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
  • level (Optional[str]): The level of the generation. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
  • version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
  • model (Optional[str]): The name of the model used for the generation.
  • model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
  • input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
  • output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
  • usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {promptTokens, completionTokens, totalTokens} and a more generic version {input, output, total, unit, inputCost, outputCost, totalCost} where unit can be of value "TOKENS", "CHARACTERS", "MILLISECONDS", "SECONDS", or "IMAGES". Refer to the docs on how to automatically infer token usage and costs in Langfuse.
  • usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
  • cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
  • prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
  • **kwargs: Additional keyword arguments to include in the generation.
Returns:

StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name = "llm-feature")

# Create a nested generation in Langfuse
generation = trace.generation(
    name="summary-generation",
    model="gpt-3.5-turbo",
    model_parameters={"maxTokens": "1000", "temperature": "0.9"},
    input=[{"role": "system", "content": "You are a helpful assistant."},
           {"role": "user", "content": "Please generate a summary of the following documents ..."}],
    metadata={"interface": "whatsapp"}
)
def span( self, *, id: Optional[str] = None, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, input: Optional[Any] = None, output: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, **kwargs) -> StatefulSpanClient:
2319    def span(
2320        self,
2321        *,
2322        id: typing.Optional[str] = None,
2323        name: typing.Optional[str] = None,
2324        start_time: typing.Optional[dt.datetime] = None,
2325        end_time: typing.Optional[dt.datetime] = None,
2326        metadata: typing.Optional[typing.Any] = None,
2327        input: typing.Optional[typing.Any] = None,
2328        output: typing.Optional[typing.Any] = None,
2329        level: typing.Optional[SpanLevel] = None,
2330        status_message: typing.Optional[str] = None,
2331        version: typing.Optional[str] = None,
2332        **kwargs,
2333    ) -> "StatefulSpanClient":
2334        """Create a span nested within the current observation or trace.
2335
2336        A span represents durations of units of work in a trace.
2337
2338        Args:
2339            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
2340            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2341            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2342            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2343            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2344            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2345            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
2346            input (Optional[dict]): The input to the span. Can be any JSON object.
2347            output (Optional[dict]): The output to the span. Can be any JSON object.
2348            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2349            **kwargs: Additional keyword arguments to include in the span.
2350
2351        Returns:
2352            StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.
2353
2354        Example:
2355            ```python
2356            from langfuse import Langfuse
2357
2358            langfuse = Langfuse()
2359
2360            # Create a trace
2361            trace = langfuse.trace(name = "llm-feature")
2362
2363            # Create a span
2364            retrieval = langfuse.span(name = "retrieval")
2365            ```
2366        """
2367        span_id = id or str(uuid.uuid4())
2368        try:
2369            span_body = {
2370                "id": span_id,
2371                "name": name,
2372                "start_time": start_time or _get_timestamp(),
2373                "metadata": metadata,
2374                "input": input,
2375                "output": output,
2376                "level": level,
2377                "status_message": status_message,
2378                "version": version,
2379                "end_time": end_time,
2380                "environment": self.environment,
2381                **kwargs,
2382            }
2383
2384            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
2385
2386            new_dict = self._add_state_to_event(span_body)
2387            new_body = self._add_default_values(new_dict)
2388
2389            event = CreateSpanBody(**new_body)
2390
2391            event = {
2392                "id": str(uuid.uuid4()),
2393                "type": "span-create",
2394                "body": event,
2395            }
2396
2397            self.task_manager.add_task(event)
2398        except Exception as e:
2399            self.log.exception(e)
2400        finally:
2401            return StatefulSpanClient(
2402                self.client,
2403                span_id,
2404                StateType.OBSERVATION,
2405                self.trace_id,
2406                self.task_manager,
2407                self.environment,
2408            )

Create a span nested within the current observation or trace.

A span represents durations of units of work in a trace.

Arguments:
  • id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
  • name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
  • end_time (Optional[datetime]): The time at which the span ended. Automatically set by span.end().
  • metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
  • level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
  • input (Optional[dict]): The input to the span. Can be any JSON object.
  • output (Optional[dict]): The output to the span. Can be any JSON object.
  • version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
  • **kwargs: Additional keyword arguments to include in the span.
Returns:

StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name = "llm-feature")

# Create a span
retrieval = langfuse.span(name = "retrieval")
def score( self, *, id: Optional[str] = None, name: str, value: Union[float, str], data_type: Optional[Literal['NUMERIC', 'CATEGORICAL', 'BOOLEAN']] = None, comment: Optional[str] = None, config_id: Optional[str] = None, **kwargs) -> StatefulClient:
2436    def score(
2437        self,
2438        *,
2439        id: typing.Optional[str] = None,
2440        name: str,
2441        value: typing.Union[float, str],
2442        data_type: typing.Optional[ScoreDataType] = None,
2443        comment: typing.Optional[str] = None,
2444        config_id: typing.Optional[str] = None,
2445        **kwargs,
2446    ) -> "StatefulClient":
2447        """Create a score attached for the current observation or trace.
2448
2449        Args:
2450            name (str): Identifier of the score.
2451            value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
2452            data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present.
2453              When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
2454            comment (Optional[str]): Additional context/explanation of the score.
2455            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
2456            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
2457            **kwargs: Additional keyword arguments to include in the score.
2458
2459        Returns:
2460            StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.
2461
2462        Example:
2463            ```python
2464            from langfuse import Langfuse
2465
2466            langfuse = Langfuse()
2467
2468            # Create a trace
2469            trace = langfuse.trace(name="example-application")
2470
2471            # Add score to the trace
2472            trace = trace.score(
2473                name="user-explicit-feedback",
2474                value=0.8,
2475                comment="I like how personalized the response is"
2476            )
2477            ```
2478        """
2479        score_id = id or str(uuid.uuid4())
2480        try:
2481            new_score = {
2482                "id": score_id,
2483                "trace_id": self.trace_id,
2484                "name": name,
2485                "value": value,
2486                "data_type": data_type,
2487                "comment": comment,
2488                "config_id": config_id,
2489                "environment": self.environment,
2490                **kwargs,
2491            }
2492
2493            self.log.debug(f"Creating score {new_score}...")
2494
2495            new_dict = self._add_state_to_event(new_score)
2496
2497            if self.state_type == StateType.OBSERVATION:
2498                new_dict["observationId"] = self.id
2499
2500            request = ScoreBody(**new_dict)
2501
2502            event = {
2503                "id": str(uuid.uuid4()),
2504                "type": "score-create",
2505                "body": request,
2506            }
2507
2508            self.task_manager.add_task(event)
2509
2510        except Exception as e:
2511            self.log.exception(e)
2512        finally:
2513            return StatefulClient(
2514                self.client,
2515                self.id,
2516                self.state_type,
2517                self.trace_id,
2518                self.task_manager,
2519                self.environment,
2520            )

Create a score attached for the current observation or trace.

Arguments:
  • name (str): Identifier of the score.
  • value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
  • data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
  • comment (Optional[str]): Additional context/explanation of the score.
  • id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
  • config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
  • **kwargs: Additional keyword arguments to include in the score.
Returns:

StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name="example-application")

# Add score to the trace
trace = trace.score(
    name="user-explicit-feedback",
    value=0.8,
    comment="I like how personalized the response is"
)
def event( self, *, id: Optional[str] = None, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, input: Optional[Any] = None, output: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, **kwargs) -> StatefulClient:
2522    def event(
2523        self,
2524        *,
2525        id: typing.Optional[str] = None,
2526        name: typing.Optional[str] = None,
2527        start_time: typing.Optional[dt.datetime] = None,
2528        metadata: typing.Optional[typing.Any] = None,
2529        input: typing.Optional[typing.Any] = None,
2530        output: typing.Optional[typing.Any] = None,
2531        level: typing.Optional[SpanLevel] = None,
2532        status_message: typing.Optional[str] = None,
2533        version: typing.Optional[str] = None,
2534        **kwargs,
2535    ) -> "StatefulClient":
2536        """Create an event nested within the current observation or trace.
2537
2538        An event represents a discrete event in a trace.
2539
2540        Args:
2541            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
2542            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
2543            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
2544            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
2545            input (Optional[Any]): The input to the event. Can be any JSON object.
2546            output (Optional[Any]): The output to the event. Can be any JSON object.
2547            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2548            status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event.
2549            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
2550            **kwargs: Additional keyword arguments to include in the event.
2551
2552        Returns:
2553            StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations.
2554
2555        Example:
2556            ```python
2557            from langfuse import Langfuse
2558
2559            langfuse = Langfuse()
2560
2561            # Create a trace
2562            trace = langfuse.trace(name = "llm-feature")
2563
2564            # Create an event
2565            retrieval = trace.event(name = "retrieval")
2566            ```
2567        """
2568        event_id = id or str(uuid.uuid4())
2569        try:
2570            event_body = {
2571                "id": event_id,
2572                "name": name,
2573                "start_time": start_time or _get_timestamp(),
2574                "metadata": metadata,
2575                "input": input,
2576                "output": output,
2577                "level": level,
2578                "status_message": status_message,
2579                "version": version,
2580                "environment": self.environment,
2581                **kwargs,
2582            }
2583
2584            new_dict = self._add_state_to_event(event_body)
2585            new_body = self._add_default_values(new_dict)
2586
2587            request = CreateEventBody(**new_body)
2588
2589            event = {
2590                "id": str(uuid.uuid4()),
2591                "type": "event-create",
2592                "body": request,
2593            }
2594
2595            self.log.debug(
2596                f"Creating event {_filter_io_from_event_body(event_body)}..."
2597            )
2598            self.task_manager.add_task(event)
2599
2600        except Exception as e:
2601            self.log.exception(e)
2602        finally:
2603            return StatefulClient(
2604                self.client,
2605                event_id,
2606                StateType.OBSERVATION,
2607                self.trace_id,
2608                self.task_manager,
2609                self.environment,
2610            )

Create an event nested within the current observation or trace.

An event represents a discrete event in a trace.

Arguments:
  • id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
  • name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
  • metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
  • input (Optional[Any]): The input to the event. Can be any JSON object.
  • output (Optional[Any]): The output to the event. Can be any JSON object.
  • level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event.
  • version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
  • **kwargs: Additional keyword arguments to include in the event.
Returns:

StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name = "llm-feature")

# Create an event
retrieval = trace.event(name = "retrieval")
def get_trace_url(self):
2612    def get_trace_url(self):
2613        """Get the URL to see the current trace in the Langfuse UI."""
2614        return f"{self.client._client_wrapper._base_url}/trace/{self.trace_id}"

Get the URL to see the current trace in the Langfuse UI.

class StatefulGenerationClient(StatefulClient):
2617class StatefulGenerationClient(StatefulClient):
2618    """Class for handling stateful operations of generations in the Langfuse system. Inherits from StatefulClient.
2619
2620    This client extends the capabilities of the StatefulClient to specifically handle generation,
2621    allowing for the creation, update, and termination of generation processes in Langfuse.
2622
2623    Attributes:
2624        client (FernLangfuse): Core interface for Langfuse API interaction.
2625        id (str): Unique identifier of the generation.
2626        state_type (StateType): Type of the stateful entity (observation or trace).
2627        trace_id (str): Id of trace associated with the generation.
2628        task_manager (TaskManager): Manager for handling asynchronous tasks.
2629    """
2630
2631    log = logging.getLogger("langfuse")
2632
2633    def __init__(
2634        self,
2635        client: FernLangfuse,
2636        id: str,
2637        state_type: StateType,
2638        trace_id: str,
2639        task_manager: TaskManager,
2640        environment: Optional[str] = None,
2641    ):
2642        """Initialize the StatefulGenerationClient."""
2643        super().__init__(client, id, state_type, trace_id, task_manager, environment)
2644
2645    # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY
2646    def update(
2647        self,
2648        *,
2649        name: typing.Optional[str] = None,
2650        start_time: typing.Optional[dt.datetime] = None,
2651        end_time: typing.Optional[dt.datetime] = None,
2652        completion_start_time: typing.Optional[dt.datetime] = None,
2653        metadata: typing.Optional[typing.Any] = None,
2654        level: typing.Optional[SpanLevel] = None,
2655        status_message: typing.Optional[str] = None,
2656        version: typing.Optional[str] = None,
2657        model: typing.Optional[str] = None,
2658        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2659        input: typing.Optional[typing.Any] = None,
2660        output: typing.Optional[typing.Any] = None,
2661        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2662        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2663        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2664        prompt: typing.Optional[PromptClient] = None,
2665        **kwargs,
2666    ) -> "StatefulGenerationClient":
2667        """Update the generation.
2668
2669        Args:
2670            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2671            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2672            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2673            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
2674            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2675            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2676            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
2677            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2678            model (Optional[str]): The name of the model used for the generation.
2679            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2680            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2681            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2682            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
2683            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
2684            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
2685            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2686            **kwargs: Additional keyword arguments to include in the generation.
2687
2688        Returns:
2689            StatefulGenerationClient: The updated generation. Passthrough for chaining.
2690
2691        Example:
2692            ```python
2693            from langfuse import Langfuse
2694
2695            langfuse = Langfuse()
2696
2697            # Create a trace
2698            trace = langfuse.trace(name = "llm-feature")
2699
2700            # Create a nested generation in Langfuse
2701            generation = trace.generation(name="summary-generation")
2702
2703            # Update the generation
2704            generation = generation.update(metadata={"interface": "whatsapp"})
2705            ```
2706        """
2707        try:
2708            generation_body = {
2709                "id": self.id,
2710                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2711                "name": name,
2712                "start_time": start_time,
2713                "metadata": metadata,
2714                "level": level,
2715                "status_message": status_message,
2716                "version": version,
2717                "end_time": end_time,
2718                "completion_start_time": completion_start_time,
2719                "model": model,
2720                "model_parameters": model_parameters,
2721                "input": input,
2722                "output": output,
2723                "usage": _convert_usage_input(usage) if usage is not None else None,
2724                "usage_details": usage_details,
2725                "cost_details": cost_details,
2726                **_create_prompt_context(prompt),
2727                **kwargs,
2728            }
2729
2730            self.log.debug(
2731                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2732            )
2733
2734            request = UpdateGenerationBody(**generation_body)
2735
2736            event = {
2737                "id": str(uuid.uuid4()),
2738                "type": "generation-update",
2739                "body": request.dict(exclude_none=True, exclude_unset=False),
2740            }
2741
2742            self.log.debug(
2743                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2744            )
2745            self.task_manager.add_task(event)
2746
2747        except Exception as e:
2748            self.log.exception(e)
2749        finally:
2750            return StatefulGenerationClient(
2751                self.client,
2752                self.id,
2753                StateType.OBSERVATION,
2754                self.trace_id,
2755                self.task_manager,
2756                self.environment,
2757            )
2758
2759    def end(
2760        self,
2761        *,
2762        name: typing.Optional[str] = None,
2763        start_time: typing.Optional[dt.datetime] = None,
2764        end_time: typing.Optional[dt.datetime] = None,
2765        completion_start_time: typing.Optional[dt.datetime] = None,
2766        metadata: typing.Optional[typing.Any] = None,
2767        level: typing.Optional[SpanLevel] = None,
2768        status_message: typing.Optional[str] = None,
2769        version: typing.Optional[str] = None,
2770        model: typing.Optional[str] = None,
2771        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2772        input: typing.Optional[typing.Any] = None,
2773        output: typing.Optional[typing.Any] = None,
2774        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2775        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2776        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2777        prompt: typing.Optional[PromptClient] = None,
2778        **kwargs,
2779    ) -> "StatefulGenerationClient":
2780        """End the generation, optionally updating its properties.
2781
2782        Args:
2783            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2784            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2785            end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
2786            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
2787            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2788            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2789            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
2790            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2791            model (Optional[str]): The name of the model used for the generation.
2792            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2793            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2794            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2795            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
2796            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
2797            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
2798            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2799            **kwargs: Additional keyword arguments to include in the generation.
2800
2801        Returns:
2802            StatefulGenerationClient: The ended generation. Passthrough for chaining.
2803
2804        Example:
2805            ```python
2806            from langfuse import Langfuse
2807
2808            langfuse = Langfuse()
2809
2810            # Create a trace
2811            trace = langfuse.trace(name = "llm-feature")
2812
2813            # Create a nested generation in Langfuse
2814            generation = trace.generation(name="summary-generation")
2815
2816            # End the generation and update its properties
2817            generation = generation.end(metadata={"interface": "whatsapp"})
2818            ```
2819        """
2820        return self.update(
2821            name=name,
2822            start_time=start_time,
2823            end_time=end_time or _get_timestamp(),
2824            metadata=metadata,
2825            level=level,
2826            status_message=status_message,
2827            version=version,
2828            completion_start_time=completion_start_time,
2829            model=model,
2830            model_parameters=model_parameters,
2831            input=input,
2832            output=output,
2833            usage=usage,
2834            usage_details=usage_details,
2835            cost_details=cost_details,
2836            prompt=prompt,
2837            **kwargs,
2838        )

Class for handling stateful operations of generations in the Langfuse system. Inherits from StatefulClient.

This client extends the capabilities of the StatefulClient to specifically handle generation, allowing for the creation, update, and termination of generation processes in Langfuse.

Attributes:
  • client (FernLangfuse): Core interface for Langfuse API interaction.
  • id (str): Unique identifier of the generation.
  • state_type (StateType): Type of the stateful entity (observation or trace).
  • trace_id (str): Id of trace associated with the generation.
  • task_manager (TaskManager): Manager for handling asynchronous tasks.
StatefulGenerationClient( client: langfuse.api.client.FernLangfuse, id: str, state_type: StateType, trace_id: str, task_manager: langfuse._task_manager.task_manager.TaskManager, environment: Optional[str] = None)
2633    def __init__(
2634        self,
2635        client: FernLangfuse,
2636        id: str,
2637        state_type: StateType,
2638        trace_id: str,
2639        task_manager: TaskManager,
2640        environment: Optional[str] = None,
2641    ):
2642        """Initialize the StatefulGenerationClient."""
2643        super().__init__(client, id, state_type, trace_id, task_manager, environment)

Initialize the StatefulGenerationClient.

log = <Logger langfuse (WARNING)>
def update( self, *, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, completion_start_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, input: Optional[Any] = None, output: Optional[Any] = None, usage: Union[pydantic.v1.main.BaseModel, langfuse.model.ModelUsage, NoneType] = 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) -> StatefulGenerationClient:
2646    def update(
2647        self,
2648        *,
2649        name: typing.Optional[str] = None,
2650        start_time: typing.Optional[dt.datetime] = None,
2651        end_time: typing.Optional[dt.datetime] = None,
2652        completion_start_time: typing.Optional[dt.datetime] = None,
2653        metadata: typing.Optional[typing.Any] = None,
2654        level: typing.Optional[SpanLevel] = None,
2655        status_message: typing.Optional[str] = None,
2656        version: typing.Optional[str] = None,
2657        model: typing.Optional[str] = None,
2658        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2659        input: typing.Optional[typing.Any] = None,
2660        output: typing.Optional[typing.Any] = None,
2661        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2662        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2663        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2664        prompt: typing.Optional[PromptClient] = None,
2665        **kwargs,
2666    ) -> "StatefulGenerationClient":
2667        """Update the generation.
2668
2669        Args:
2670            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2671            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2672            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2673            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
2674            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2675            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2676            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
2677            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2678            model (Optional[str]): The name of the model used for the generation.
2679            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2680            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2681            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2682            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
2683            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
2684            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
2685            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2686            **kwargs: Additional keyword arguments to include in the generation.
2687
2688        Returns:
2689            StatefulGenerationClient: The updated generation. Passthrough for chaining.
2690
2691        Example:
2692            ```python
2693            from langfuse import Langfuse
2694
2695            langfuse = Langfuse()
2696
2697            # Create a trace
2698            trace = langfuse.trace(name = "llm-feature")
2699
2700            # Create a nested generation in Langfuse
2701            generation = trace.generation(name="summary-generation")
2702
2703            # Update the generation
2704            generation = generation.update(metadata={"interface": "whatsapp"})
2705            ```
2706        """
2707        try:
2708            generation_body = {
2709                "id": self.id,
2710                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2711                "name": name,
2712                "start_time": start_time,
2713                "metadata": metadata,
2714                "level": level,
2715                "status_message": status_message,
2716                "version": version,
2717                "end_time": end_time,
2718                "completion_start_time": completion_start_time,
2719                "model": model,
2720                "model_parameters": model_parameters,
2721                "input": input,
2722                "output": output,
2723                "usage": _convert_usage_input(usage) if usage is not None else None,
2724                "usage_details": usage_details,
2725                "cost_details": cost_details,
2726                **_create_prompt_context(prompt),
2727                **kwargs,
2728            }
2729
2730            self.log.debug(
2731                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2732            )
2733
2734            request = UpdateGenerationBody(**generation_body)
2735
2736            event = {
2737                "id": str(uuid.uuid4()),
2738                "type": "generation-update",
2739                "body": request.dict(exclude_none=True, exclude_unset=False),
2740            }
2741
2742            self.log.debug(
2743                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2744            )
2745            self.task_manager.add_task(event)
2746
2747        except Exception as e:
2748            self.log.exception(e)
2749        finally:
2750            return StatefulGenerationClient(
2751                self.client,
2752                self.id,
2753                StateType.OBSERVATION,
2754                self.trace_id,
2755                self.task_manager,
2756                self.environment,
2757            )

Update the generation.

Arguments:
  • name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
  • end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by generation.end().
  • completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
  • metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
  • level (Optional[str]): The level of the generation. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
  • version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
  • model (Optional[str]): The name of the model used for the generation.
  • model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
  • input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
  • output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
  • usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {promptTokens, completionTokens, totalTokens} and a more generic version {input, output, total, unit, inputCost, outputCost, totalCost} where unit can be of value "TOKENS", "CHARACTERS", "MILLISECONDS", "SECONDS", or "IMAGES". Refer to the docs on how to automatically infer token usage and costs in Langfuse.
  • usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
  • cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
  • prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
  • **kwargs: Additional keyword arguments to include in the generation.
Returns:

StatefulGenerationClient: The updated generation. Passthrough for chaining.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name = "llm-feature")

# Create a nested generation in Langfuse
generation = trace.generation(name="summary-generation")

# Update the generation
generation = generation.update(metadata={"interface": "whatsapp"})
def end( self, *, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, completion_start_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, input: Optional[Any] = None, output: Optional[Any] = None, usage: Union[pydantic.v1.main.BaseModel, langfuse.model.ModelUsage, NoneType] = 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) -> StatefulGenerationClient:
2759    def end(
2760        self,
2761        *,
2762        name: typing.Optional[str] = None,
2763        start_time: typing.Optional[dt.datetime] = None,
2764        end_time: typing.Optional[dt.datetime] = None,
2765        completion_start_time: typing.Optional[dt.datetime] = None,
2766        metadata: typing.Optional[typing.Any] = None,
2767        level: typing.Optional[SpanLevel] = None,
2768        status_message: typing.Optional[str] = None,
2769        version: typing.Optional[str] = None,
2770        model: typing.Optional[str] = None,
2771        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2772        input: typing.Optional[typing.Any] = None,
2773        output: typing.Optional[typing.Any] = None,
2774        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2775        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2776        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2777        prompt: typing.Optional[PromptClient] = None,
2778        **kwargs,
2779    ) -> "StatefulGenerationClient":
2780        """End the generation, optionally updating its properties.
2781
2782        Args:
2783            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2784            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2785            end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
2786            completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
2787            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2788            level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2789            status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
2790            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2791            model (Optional[str]): The name of the model used for the generation.
2792            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2793            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2794            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2795            usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse.
2796            usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
2797            cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
2798            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2799            **kwargs: Additional keyword arguments to include in the generation.
2800
2801        Returns:
2802            StatefulGenerationClient: The ended generation. Passthrough for chaining.
2803
2804        Example:
2805            ```python
2806            from langfuse import Langfuse
2807
2808            langfuse = Langfuse()
2809
2810            # Create a trace
2811            trace = langfuse.trace(name = "llm-feature")
2812
2813            # Create a nested generation in Langfuse
2814            generation = trace.generation(name="summary-generation")
2815
2816            # End the generation and update its properties
2817            generation = generation.end(metadata={"interface": "whatsapp"})
2818            ```
2819        """
2820        return self.update(
2821            name=name,
2822            start_time=start_time,
2823            end_time=end_time or _get_timestamp(),
2824            metadata=metadata,
2825            level=level,
2826            status_message=status_message,
2827            version=version,
2828            completion_start_time=completion_start_time,
2829            model=model,
2830            model_parameters=model_parameters,
2831            input=input,
2832            output=output,
2833            usage=usage,
2834            usage_details=usage_details,
2835            cost_details=cost_details,
2836            prompt=prompt,
2837            **kwargs,
2838        )

End the generation, optionally updating its properties.

Arguments:
  • name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
  • end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
  • completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
  • metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
  • level (Optional[str]): The level of the generation. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
  • version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
  • model (Optional[str]): The name of the model used for the generation.
  • model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
  • input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
  • output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
  • usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {promptTokens, completionTokens, totalTokens} and a more generic version {input, output, total, unit, inputCost, outputCost, totalCost} where unit can be of value "TOKENS", "CHARACTERS", "MILLISECONDS", "SECONDS", or "IMAGES". Refer to the docs on how to automatically infer token usage and costs in Langfuse.
  • usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
  • cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
  • prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
  • **kwargs: Additional keyword arguments to include in the generation.
Returns:

StatefulGenerationClient: The ended generation. Passthrough for chaining.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name = "llm-feature")

# Create a nested generation in Langfuse
generation = trace.generation(name="summary-generation")

# End the generation and update its properties
generation = generation.end(metadata={"interface": "whatsapp"})
class StatefulSpanClient(StatefulClient):
2841class StatefulSpanClient(StatefulClient):
2842    """Class for handling stateful operations of spans in the Langfuse system. Inherits from StatefulClient.
2843
2844    Attributes:
2845        client (FernLangfuse): Core interface for Langfuse API interaction.
2846        id (str): Unique identifier of the span.
2847        state_type (StateType): Type of the stateful entity (observation or trace).
2848        trace_id (str): Id of trace associated with the span.
2849        task_manager (TaskManager): Manager for handling asynchronous tasks.
2850    """
2851
2852    log = logging.getLogger("langfuse")
2853
2854    def __init__(
2855        self,
2856        client: FernLangfuse,
2857        id: str,
2858        state_type: StateType,
2859        trace_id: str,
2860        task_manager: TaskManager,
2861        environment: Optional[str] = None,
2862    ):
2863        """Initialize the StatefulSpanClient."""
2864        super().__init__(client, id, state_type, trace_id, task_manager, environment)
2865
2866    # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY
2867    def update(
2868        self,
2869        *,
2870        name: typing.Optional[str] = None,
2871        start_time: typing.Optional[dt.datetime] = None,
2872        end_time: typing.Optional[dt.datetime] = None,
2873        metadata: typing.Optional[typing.Any] = None,
2874        input: typing.Optional[typing.Any] = None,
2875        output: typing.Optional[typing.Any] = None,
2876        level: typing.Optional[SpanLevel] = None,
2877        status_message: typing.Optional[str] = None,
2878        version: typing.Optional[str] = None,
2879        **kwargs,
2880    ) -> "StatefulSpanClient":
2881        """Update the span.
2882
2883        Args:
2884            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2885            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2886            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2887            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2888            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2889            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
2890            input (Optional[dict]): The input to the span. Can be any JSON object.
2891            output (Optional[dict]): The output to the span. Can be any JSON object.
2892            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2893            **kwargs: Additional keyword arguments to include in the span.
2894
2895        Returns:
2896            StatefulSpanClient: The updated span. Passthrough for chaining.
2897
2898        Example:
2899            ```python
2900            from langfuse import Langfuse
2901
2902            langfuse = Langfuse()
2903
2904            # Create a trace
2905            trace = langfuse.trace(name = "llm-feature")
2906
2907            # Create a nested span in Langfuse
2908            span = trace.span(name="retrieval")
2909
2910            # Update the span
2911            span = span.update(metadata={"interface": "whatsapp"})
2912            ```
2913        """
2914        try:
2915            span_body = {
2916                "id": self.id,
2917                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2918                "name": name,
2919                "start_time": start_time,
2920                "metadata": metadata,
2921                "input": input,
2922                "output": output,
2923                "level": level,
2924                "status_message": status_message,
2925                "version": version,
2926                "end_time": end_time,
2927                **kwargs,
2928            }
2929            self.log.debug(f"Update span {_filter_io_from_event_body(span_body)}...")
2930
2931            request = UpdateSpanBody(**span_body)
2932
2933            event = {
2934                "id": str(uuid.uuid4()),
2935                "type": "span-update",
2936                "body": request,
2937            }
2938
2939            self.task_manager.add_task(event)
2940        except Exception as e:
2941            self.log.exception(e)
2942        finally:
2943            return StatefulSpanClient(
2944                self.client,
2945                self.id,
2946                StateType.OBSERVATION,
2947                self.trace_id,
2948                self.task_manager,
2949                self.environment,
2950            )
2951
2952    def end(
2953        self,
2954        *,
2955        name: typing.Optional[str] = None,
2956        start_time: typing.Optional[dt.datetime] = None,
2957        end_time: typing.Optional[dt.datetime] = None,
2958        metadata: typing.Optional[typing.Any] = None,
2959        input: typing.Optional[typing.Any] = None,
2960        output: typing.Optional[typing.Any] = None,
2961        level: typing.Optional[SpanLevel] = None,
2962        status_message: typing.Optional[str] = None,
2963        version: typing.Optional[str] = None,
2964        **kwargs,
2965    ) -> "StatefulSpanClient":
2966        """End the span, optionally updating its properties.
2967
2968        Args:
2969            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2970            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2971            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2972            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2973            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2974            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
2975            input (Optional[dict]): The input to the span. Can be any JSON object.
2976            output (Optional[dict]): The output to the span. Can be any JSON object.
2977            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2978            **kwargs: Additional keyword arguments to include in the span.
2979
2980        Returns:
2981            StatefulSpanClient: The updated span. Passthrough for chaining.
2982
2983        Example:
2984            ```python
2985            from langfuse import Langfuse
2986
2987            langfuse = Langfuse()
2988
2989            # Create a trace
2990            trace = langfuse.trace(name = "llm-feature")
2991
2992            # Create a nested span in Langfuse
2993            span = trace.span(name="retrieval")
2994
2995            # End the span and update its properties
2996            span = span.end(metadata={"interface": "whatsapp"})
2997            ```
2998        """
2999        try:
3000            span_body = {
3001                "name": name,
3002                "start_time": start_time,
3003                "metadata": metadata,
3004                "input": input,
3005                "output": output,
3006                "level": level,
3007                "status_message": status_message,
3008                "version": version,
3009                "end_time": end_time or _get_timestamp(),
3010                **kwargs,
3011            }
3012            return self.update(**span_body)
3013
3014        except Exception as e:
3015            self.log.warning(e)
3016        finally:
3017            return StatefulSpanClient(
3018                self.client,
3019                self.id,
3020                StateType.OBSERVATION,
3021                self.trace_id,
3022                self.task_manager,
3023                self.environment,
3024            )
3025
3026    def get_langchain_handler(self, update_parent: bool = False):
3027        """Get langchain callback handler associated with the current span.
3028
3029        Args:
3030            update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
3031
3032        Returns:
3033            CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
3034        """
3035        from langfuse.callback import CallbackHandler
3036
3037        return CallbackHandler(
3038            stateful_client=self, update_stateful_client=update_parent
3039        )

Class for handling stateful operations of spans in the Langfuse system. Inherits from StatefulClient.

Attributes:
  • client (FernLangfuse): Core interface for Langfuse API interaction.
  • id (str): Unique identifier of the span.
  • state_type (StateType): Type of the stateful entity (observation or trace).
  • trace_id (str): Id of trace associated with the span.
  • task_manager (TaskManager): Manager for handling asynchronous tasks.
StatefulSpanClient( client: langfuse.api.client.FernLangfuse, id: str, state_type: StateType, trace_id: str, task_manager: langfuse._task_manager.task_manager.TaskManager, environment: Optional[str] = None)
2854    def __init__(
2855        self,
2856        client: FernLangfuse,
2857        id: str,
2858        state_type: StateType,
2859        trace_id: str,
2860        task_manager: TaskManager,
2861        environment: Optional[str] = None,
2862    ):
2863        """Initialize the StatefulSpanClient."""
2864        super().__init__(client, id, state_type, trace_id, task_manager, environment)

Initialize the StatefulSpanClient.

log = <Logger langfuse (WARNING)>
def update( self, *, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, input: Optional[Any] = None, output: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, **kwargs) -> StatefulSpanClient:
2867    def update(
2868        self,
2869        *,
2870        name: typing.Optional[str] = None,
2871        start_time: typing.Optional[dt.datetime] = None,
2872        end_time: typing.Optional[dt.datetime] = None,
2873        metadata: typing.Optional[typing.Any] = None,
2874        input: typing.Optional[typing.Any] = None,
2875        output: typing.Optional[typing.Any] = None,
2876        level: typing.Optional[SpanLevel] = None,
2877        status_message: typing.Optional[str] = None,
2878        version: typing.Optional[str] = None,
2879        **kwargs,
2880    ) -> "StatefulSpanClient":
2881        """Update the span.
2882
2883        Args:
2884            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2885            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2886            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2887            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2888            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2889            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
2890            input (Optional[dict]): The input to the span. Can be any JSON object.
2891            output (Optional[dict]): The output to the span. Can be any JSON object.
2892            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2893            **kwargs: Additional keyword arguments to include in the span.
2894
2895        Returns:
2896            StatefulSpanClient: The updated span. Passthrough for chaining.
2897
2898        Example:
2899            ```python
2900            from langfuse import Langfuse
2901
2902            langfuse = Langfuse()
2903
2904            # Create a trace
2905            trace = langfuse.trace(name = "llm-feature")
2906
2907            # Create a nested span in Langfuse
2908            span = trace.span(name="retrieval")
2909
2910            # Update the span
2911            span = span.update(metadata={"interface": "whatsapp"})
2912            ```
2913        """
2914        try:
2915            span_body = {
2916                "id": self.id,
2917                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2918                "name": name,
2919                "start_time": start_time,
2920                "metadata": metadata,
2921                "input": input,
2922                "output": output,
2923                "level": level,
2924                "status_message": status_message,
2925                "version": version,
2926                "end_time": end_time,
2927                **kwargs,
2928            }
2929            self.log.debug(f"Update span {_filter_io_from_event_body(span_body)}...")
2930
2931            request = UpdateSpanBody(**span_body)
2932
2933            event = {
2934                "id": str(uuid.uuid4()),
2935                "type": "span-update",
2936                "body": request,
2937            }
2938
2939            self.task_manager.add_task(event)
2940        except Exception as e:
2941            self.log.exception(e)
2942        finally:
2943            return StatefulSpanClient(
2944                self.client,
2945                self.id,
2946                StateType.OBSERVATION,
2947                self.trace_id,
2948                self.task_manager,
2949                self.environment,
2950            )

Update the span.

Arguments:
  • name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
  • end_time (Optional[datetime]): The time at which the span ended. Automatically set by span.end().
  • metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
  • level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
  • input (Optional[dict]): The input to the span. Can be any JSON object.
  • output (Optional[dict]): The output to the span. Can be any JSON object.
  • version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
  • **kwargs: Additional keyword arguments to include in the span.
Returns:

StatefulSpanClient: The updated span. Passthrough for chaining.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name = "llm-feature")

# Create a nested span in Langfuse
span = trace.span(name="retrieval")

# Update the span
span = span.update(metadata={"interface": "whatsapp"})
def end( self, *, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, input: Optional[Any] = None, output: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, **kwargs) -> StatefulSpanClient:
2952    def end(
2953        self,
2954        *,
2955        name: typing.Optional[str] = None,
2956        start_time: typing.Optional[dt.datetime] = None,
2957        end_time: typing.Optional[dt.datetime] = None,
2958        metadata: typing.Optional[typing.Any] = None,
2959        input: typing.Optional[typing.Any] = None,
2960        output: typing.Optional[typing.Any] = None,
2961        level: typing.Optional[SpanLevel] = None,
2962        status_message: typing.Optional[str] = None,
2963        version: typing.Optional[str] = None,
2964        **kwargs,
2965    ) -> "StatefulSpanClient":
2966        """End the span, optionally updating its properties.
2967
2968        Args:
2969            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2970            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2971            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2972            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2973            level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
2974            status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
2975            input (Optional[dict]): The input to the span. Can be any JSON object.
2976            output (Optional[dict]): The output to the span. Can be any JSON object.
2977            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2978            **kwargs: Additional keyword arguments to include in the span.
2979
2980        Returns:
2981            StatefulSpanClient: The updated span. Passthrough for chaining.
2982
2983        Example:
2984            ```python
2985            from langfuse import Langfuse
2986
2987            langfuse = Langfuse()
2988
2989            # Create a trace
2990            trace = langfuse.trace(name = "llm-feature")
2991
2992            # Create a nested span in Langfuse
2993            span = trace.span(name="retrieval")
2994
2995            # End the span and update its properties
2996            span = span.end(metadata={"interface": "whatsapp"})
2997            ```
2998        """
2999        try:
3000            span_body = {
3001                "name": name,
3002                "start_time": start_time,
3003                "metadata": metadata,
3004                "input": input,
3005                "output": output,
3006                "level": level,
3007                "status_message": status_message,
3008                "version": version,
3009                "end_time": end_time or _get_timestamp(),
3010                **kwargs,
3011            }
3012            return self.update(**span_body)
3013
3014        except Exception as e:
3015            self.log.warning(e)
3016        finally:
3017            return StatefulSpanClient(
3018                self.client,
3019                self.id,
3020                StateType.OBSERVATION,
3021                self.trace_id,
3022                self.task_manager,
3023                self.environment,
3024            )

End the span, optionally updating its properties.

Arguments:
  • name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
  • start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
  • end_time (Optional[datetime]): The time at which the span ended. Automatically set by span.end().
  • metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
  • level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be DEBUG, DEFAULT, WARNING or ERROR. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI.
  • status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event.
  • input (Optional[dict]): The input to the span. Can be any JSON object.
  • output (Optional[dict]): The output to the span. Can be any JSON object.
  • version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
  • **kwargs: Additional keyword arguments to include in the span.
Returns:

StatefulSpanClient: The updated span. Passthrough for chaining.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name = "llm-feature")

# Create a nested span in Langfuse
span = trace.span(name="retrieval")

# End the span and update its properties
span = span.end(metadata={"interface": "whatsapp"})
def get_langchain_handler(self, update_parent: bool = False):
3026    def get_langchain_handler(self, update_parent: bool = False):
3027        """Get langchain callback handler associated with the current span.
3028
3029        Args:
3030            update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
3031
3032        Returns:
3033            CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
3034        """
3035        from langfuse.callback import CallbackHandler
3036
3037        return CallbackHandler(
3038            stateful_client=self, update_stateful_client=update_parent
3039        )

Get langchain callback handler associated with the current span.

Arguments:
  • update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
Returns:

CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.

class StatefulTraceClient(StatefulClient):
3042class StatefulTraceClient(StatefulClient):
3043    """Class for handling stateful operations of traces in the Langfuse system. Inherits from StatefulClient.
3044
3045    Attributes:
3046        client (FernLangfuse): Core interface for Langfuse API interaction.
3047        id (str): Unique identifier of the trace.
3048        state_type (StateType): Type of the stateful entity (observation or trace).
3049        trace_id (str): The trace ID associated with this client.
3050        task_manager (TaskManager): Manager for handling asynchronous tasks.
3051    """
3052
3053    log = logging.getLogger("langfuse")
3054
3055    def __init__(
3056        self,
3057        client: FernLangfuse,
3058        id: str,
3059        state_type: StateType,
3060        trace_id: str,
3061        task_manager: TaskManager,
3062        environment: Optional[str] = None,
3063    ):
3064        """Initialize the StatefulTraceClient."""
3065        super().__init__(client, id, state_type, trace_id, task_manager, environment)
3066        self.task_manager = task_manager
3067
3068    def update(
3069        self,
3070        *,
3071        name: typing.Optional[str] = None,
3072        user_id: typing.Optional[str] = None,
3073        session_id: typing.Optional[str] = None,
3074        version: typing.Optional[str] = None,
3075        release: typing.Optional[str] = None,
3076        input: typing.Optional[typing.Any] = None,
3077        output: typing.Optional[typing.Any] = None,
3078        metadata: typing.Optional[typing.Any] = None,
3079        tags: typing.Optional[typing.List[str]] = None,
3080        public: typing.Optional[bool] = None,
3081        **kwargs,
3082    ) -> "StatefulTraceClient":
3083        """Update the trace.
3084
3085        Args:
3086            name: Identifier of the trace. Useful for sorting/filtering in the UI.
3087            input: The input of the trace. Can be any JSON object.
3088            output: The output of the trace. Can be any JSON object.
3089            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
3090            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
3091            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
3092            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
3093            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
3094            tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
3095            public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project.
3096            **kwargs: Additional keyword arguments that can be included in the trace.
3097
3098        Returns:
3099            StatefulTraceClient: The updated trace. Passthrough for chaining.
3100
3101        Example:
3102            ```python
3103            from langfuse import Langfuse
3104
3105            langfuse = Langfuse()
3106
3107            # Create a trace
3108            trace = langfuse.trace(
3109                name="example-application",
3110                user_id="user-1234")
3111            )
3112
3113            # Update the trace
3114            trace = trace.update(
3115                output={"result": "success"},
3116                metadata={"interface": "whatsapp"}
3117            )
3118            ```
3119        """
3120        try:
3121            trace_body = {
3122                "id": self.id,
3123                "name": name,
3124                "userId": user_id,
3125                "sessionId": session_id
3126                or kwargs.get("sessionId", None),  # backward compatibility
3127                "version": version,
3128                "release": release,
3129                "input": input,
3130                "output": output,
3131                "metadata": metadata,
3132                "public": public,
3133                "tags": tags,
3134                **kwargs,
3135            }
3136            self.log.debug(f"Update trace {_filter_io_from_event_body(trace_body)}...")
3137
3138            request = TraceBody(**trace_body)
3139
3140            event = {
3141                "id": str(uuid.uuid4()),
3142                "type": "trace-create",
3143                "body": request,
3144            }
3145
3146            self.task_manager.add_task(event)
3147
3148        except Exception as e:
3149            self.log.exception(e)
3150        finally:
3151            return StatefulTraceClient(
3152                self.client,
3153                self.id,
3154                StateType.TRACE,
3155                self.trace_id,
3156                self.task_manager,
3157                self.environment,
3158            )
3159
3160    def get_langchain_handler(self, update_parent: bool = False):
3161        """Get langchain callback handler associated with the current trace.
3162
3163        This method creates and returns a CallbackHandler instance, linking it with the current
3164        trace. Use this if you want to group multiple Langchain runs within a single trace.
3165
3166        Args:
3167            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
3168
3169        Raises:
3170            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
3171
3172        Returns:
3173            CallbackHandler: Langchain callback handler linked to the current trace.
3174
3175        Example:
3176            ```python
3177            from langfuse import Langfuse
3178
3179            langfuse = Langfuse()
3180
3181            # Create a trace
3182            trace = langfuse.trace(name = "llm-feature")
3183
3184            # Get a langchain callback handler
3185            handler = trace.get_langchain_handler()
3186            ```
3187        """
3188        try:
3189            from langfuse.callback import CallbackHandler
3190
3191            self.log.debug(f"Creating new handler for trace {self.id}")
3192
3193            return CallbackHandler(
3194                stateful_client=self,
3195                debug=self.log.level == logging.DEBUG,
3196                update_stateful_client=update_parent,
3197            )
3198        except Exception as e:
3199            self.log.exception(e)
3200
3201    def getNewHandler(self):
3202        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
3203        return self.get_langchain_handler()

Class for handling stateful operations of traces in the Langfuse system. Inherits from StatefulClient.

Attributes:
  • client (FernLangfuse): Core interface for Langfuse API interaction.
  • id (str): Unique identifier of the trace.
  • state_type (StateType): Type of the stateful entity (observation or trace).
  • trace_id (str): The trace ID associated with this client.
  • task_manager (TaskManager): Manager for handling asynchronous tasks.
StatefulTraceClient( client: langfuse.api.client.FernLangfuse, id: str, state_type: StateType, trace_id: str, task_manager: langfuse._task_manager.task_manager.TaskManager, environment: Optional[str] = None)
3055    def __init__(
3056        self,
3057        client: FernLangfuse,
3058        id: str,
3059        state_type: StateType,
3060        trace_id: str,
3061        task_manager: TaskManager,
3062        environment: Optional[str] = None,
3063    ):
3064        """Initialize the StatefulTraceClient."""
3065        super().__init__(client, id, state_type, trace_id, task_manager, environment)
3066        self.task_manager = task_manager

Initialize the StatefulTraceClient.

log = <Logger langfuse (WARNING)>
task_manager
def update( self, *, name: Optional[str] = None, user_id: Optional[str] = None, session_id: Optional[str] = None, version: Optional[str] = None, release: Optional[str] = None, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, tags: Optional[List[str]] = None, public: Optional[bool] = None, **kwargs) -> StatefulTraceClient:
3068    def update(
3069        self,
3070        *,
3071        name: typing.Optional[str] = None,
3072        user_id: typing.Optional[str] = None,
3073        session_id: typing.Optional[str] = None,
3074        version: typing.Optional[str] = None,
3075        release: typing.Optional[str] = None,
3076        input: typing.Optional[typing.Any] = None,
3077        output: typing.Optional[typing.Any] = None,
3078        metadata: typing.Optional[typing.Any] = None,
3079        tags: typing.Optional[typing.List[str]] = None,
3080        public: typing.Optional[bool] = None,
3081        **kwargs,
3082    ) -> "StatefulTraceClient":
3083        """Update the trace.
3084
3085        Args:
3086            name: Identifier of the trace. Useful for sorting/filtering in the UI.
3087            input: The input of the trace. Can be any JSON object.
3088            output: The output of the trace. Can be any JSON object.
3089            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
3090            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
3091            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
3092            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
3093            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
3094            tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
3095            public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project.
3096            **kwargs: Additional keyword arguments that can be included in the trace.
3097
3098        Returns:
3099            StatefulTraceClient: The updated trace. Passthrough for chaining.
3100
3101        Example:
3102            ```python
3103            from langfuse import Langfuse
3104
3105            langfuse = Langfuse()
3106
3107            # Create a trace
3108            trace = langfuse.trace(
3109                name="example-application",
3110                user_id="user-1234")
3111            )
3112
3113            # Update the trace
3114            trace = trace.update(
3115                output={"result": "success"},
3116                metadata={"interface": "whatsapp"}
3117            )
3118            ```
3119        """
3120        try:
3121            trace_body = {
3122                "id": self.id,
3123                "name": name,
3124                "userId": user_id,
3125                "sessionId": session_id
3126                or kwargs.get("sessionId", None),  # backward compatibility
3127                "version": version,
3128                "release": release,
3129                "input": input,
3130                "output": output,
3131                "metadata": metadata,
3132                "public": public,
3133                "tags": tags,
3134                **kwargs,
3135            }
3136            self.log.debug(f"Update trace {_filter_io_from_event_body(trace_body)}...")
3137
3138            request = TraceBody(**trace_body)
3139
3140            event = {
3141                "id": str(uuid.uuid4()),
3142                "type": "trace-create",
3143                "body": request,
3144            }
3145
3146            self.task_manager.add_task(event)
3147
3148        except Exception as e:
3149            self.log.exception(e)
3150        finally:
3151            return StatefulTraceClient(
3152                self.client,
3153                self.id,
3154                StateType.TRACE,
3155                self.trace_id,
3156                self.task_manager,
3157                self.environment,
3158            )

Update the trace.

Arguments:
  • name: Identifier of the trace. Useful for sorting/filtering in the UI.
  • input: The input of the trace. Can be any JSON object.
  • output: The output of the trace. Can be any JSON object.
  • metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
  • user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
  • session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
  • version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
  • release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
  • tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
  • public: You can make a trace public to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project.
  • **kwargs: Additional keyword arguments that can be included in the trace.
Returns:

StatefulTraceClient: The updated trace. Passthrough for chaining.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(
    name="example-application",
    user_id="user-1234")
)

# Update the trace
trace = trace.update(
    output={"result": "success"},
    metadata={"interface": "whatsapp"}
)
def get_langchain_handler(self, update_parent: bool = False):
3160    def get_langchain_handler(self, update_parent: bool = False):
3161        """Get langchain callback handler associated with the current trace.
3162
3163        This method creates and returns a CallbackHandler instance, linking it with the current
3164        trace. Use this if you want to group multiple Langchain runs within a single trace.
3165
3166        Args:
3167            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
3168
3169        Raises:
3170            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
3171
3172        Returns:
3173            CallbackHandler: Langchain callback handler linked to the current trace.
3174
3175        Example:
3176            ```python
3177            from langfuse import Langfuse
3178
3179            langfuse = Langfuse()
3180
3181            # Create a trace
3182            trace = langfuse.trace(name = "llm-feature")
3183
3184            # Get a langchain callback handler
3185            handler = trace.get_langchain_handler()
3186            ```
3187        """
3188        try:
3189            from langfuse.callback import CallbackHandler
3190
3191            self.log.debug(f"Creating new handler for trace {self.id}")
3192
3193            return CallbackHandler(
3194                stateful_client=self,
3195                debug=self.log.level == logging.DEBUG,
3196                update_stateful_client=update_parent,
3197            )
3198        except Exception as e:
3199            self.log.exception(e)

Get langchain callback handler associated with the current trace.

This method creates and returns a CallbackHandler instance, linking it with the current trace. Use this if you want to group multiple Langchain runs within a single trace.

Arguments:
  • update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
Raises:
  • ImportError: If the 'langchain' module is not installed, indicating missing functionality.
Returns:

CallbackHandler: Langchain callback handler linked to the current trace.

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace
trace = langfuse.trace(name = "llm-feature")

# Get a langchain callback handler
handler = trace.get_langchain_handler()
def getNewHandler(self):
3201    def getNewHandler(self):
3202        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
3203        return self.get_langchain_handler()

Alias for the get_langchain_handler method. Retrieves a callback handler for the trace. Deprecated.

class DatasetItemClient:
3206class DatasetItemClient:
3207    """Class for managing dataset items in Langfuse.
3208
3209    Args:
3210        id (str): Unique identifier of the dataset item.
3211        status (DatasetStatus): The status of the dataset item. Can be either 'ACTIVE' or 'ARCHIVED'.
3212        input (Any): Input data of the dataset item.
3213        expected_output (Optional[Any]): Expected output of the dataset item.
3214        metadata (Optional[Any]): Additional metadata of the dataset item.
3215        source_trace_id (Optional[str]): Identifier of the source trace.
3216        source_observation_id (Optional[str]): Identifier of the source observation.
3217        dataset_id (str): Identifier of the dataset to which this item belongs.
3218        dataset_name (str): Name of the dataset to which this item belongs.
3219        created_at (datetime): Timestamp of dataset item creation.
3220        updated_at (datetime): Timestamp of the last update to the dataset item.
3221        langfuse (Langfuse): Instance of Langfuse client for API interactions.
3222
3223    Example:
3224        ```python
3225        from langfuse import Langfuse
3226
3227        langfuse = Langfuse()
3228
3229        dataset = langfuse.get_dataset("<dataset_name>")
3230
3231        for item in dataset.items:
3232            # Generate a completion using the input of every item
3233            completion, generation = llm_app.run(item.input)
3234
3235            # Evaluate the completion
3236            generation.score(
3237                name="example-score",
3238                value=1
3239            )
3240        ```
3241    """
3242
3243    log = logging.getLogger("langfuse")
3244
3245    id: str
3246    status: DatasetStatus
3247    input: typing.Any
3248    expected_output: typing.Optional[typing.Any]
3249    metadata: Optional[Any]
3250    source_trace_id: typing.Optional[str]
3251    source_observation_id: typing.Optional[str]
3252    dataset_id: str
3253    dataset_name: str
3254    created_at: dt.datetime
3255    updated_at: dt.datetime
3256
3257    langfuse: Langfuse
3258
3259    def __init__(self, dataset_item: DatasetItem, langfuse: Langfuse):
3260        """Initialize the DatasetItemClient."""
3261        self.id = dataset_item.id
3262        self.status = dataset_item.status
3263        self.input = dataset_item.input
3264        self.expected_output = dataset_item.expected_output
3265        self.metadata = dataset_item.metadata
3266        self.source_trace_id = dataset_item.source_trace_id
3267        self.source_observation_id = dataset_item.source_observation_id
3268        self.dataset_id = dataset_item.dataset_id
3269        self.dataset_name = dataset_item.dataset_name
3270        self.created_at = dataset_item.created_at
3271        self.updated_at = dataset_item.updated_at
3272
3273        self.langfuse = langfuse
3274
3275    def flush(self, observation: StatefulClient, run_name: str):
3276        """Flushes an observations task manager's queue.
3277
3278        Used before creating a dataset run item to ensure all events are persistent.
3279
3280        Args:
3281            observation (StatefulClient): The observation or trace client associated with the dataset item.
3282            run_name (str): The name of the dataset run.
3283        """
3284        observation.task_manager.flush()
3285
3286    def link(
3287        self,
3288        trace_or_observation: typing.Union[StatefulClient, str, None],
3289        run_name: str,
3290        run_metadata: Optional[Any] = None,
3291        run_description: Optional[str] = None,
3292        trace_id: Optional[str] = None,
3293        observation_id: Optional[str] = None,
3294    ):
3295        """Link the dataset item to observation within a specific dataset run. Creates a dataset run item.
3296
3297        Args:
3298            trace_or_observation (Union[StatefulClient, str, None]): The trace or observation object to link. Deprecated: can also be an observation ID.
3299            run_name (str): The name of the dataset run.
3300            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3301            run_description (Optional[str]): Description of the dataset run.
3302            trace_id (Optional[str]): The trace ID to link to the dataset item. Set trace_or_observation to None if trace_id is provided.
3303            observation_id (Optional[str]): The observation ID to link to the dataset item (optional). Set trace_or_observation to None if trace_id is provided.
3304        """
3305        parsed_trace_id: str = None
3306        parsed_observation_id: str = None
3307
3308        if isinstance(trace_or_observation, StatefulClient):
3309            # flush the queue before creating the dataset run item
3310            # to ensure that all events are persisted.
3311            if trace_or_observation.state_type == StateType.TRACE:
3312                parsed_trace_id = trace_or_observation.trace_id
3313            elif trace_or_observation.state_type == StateType.OBSERVATION:
3314                parsed_observation_id = trace_or_observation.id
3315                parsed_trace_id = trace_or_observation.trace_id
3316        # legacy support for observation_id
3317        elif isinstance(trace_or_observation, str):
3318            parsed_observation_id = trace_or_observation
3319        elif trace_or_observation is None:
3320            if trace_id is not None:
3321                parsed_trace_id = trace_id
3322                if observation_id is not None:
3323                    parsed_observation_id = observation_id
3324            else:
3325                raise ValueError(
3326                    "trace_id must be provided if trace_or_observation is None"
3327                )
3328        else:
3329            raise ValueError(
3330                "trace_or_observation (arg) or trace_id (kwarg) must be provided to link the dataset item"
3331            )
3332
3333        self.log.debug(
3334            f"Creating dataset run item: {run_name} {self.id} {parsed_trace_id} {parsed_observation_id}"
3335        )
3336        self.langfuse.client.dataset_run_items.create(
3337            request=CreateDatasetRunItemRequest(
3338                runName=run_name,
3339                datasetItemId=self.id,
3340                traceId=parsed_trace_id,
3341                observationId=parsed_observation_id,
3342                metadata=run_metadata,
3343                runDescription=run_description,
3344            )
3345        )
3346
3347    def get_langchain_handler(
3348        self,
3349        *,
3350        run_name: str,
3351        run_description: Optional[str] = None,
3352        run_metadata: Optional[Any] = None,
3353    ):
3354        """Create and get a langchain callback handler linked to this dataset item.
3355
3356        Args:
3357            run_name (str): The name of the dataset run to be used in the callback handler.
3358            run_description (Optional[str]): Description of the dataset run.
3359            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3360
3361        Returns:
3362            CallbackHandler: An instance of CallbackHandler linked to the dataset item.
3363        """
3364        metadata = {
3365            "dataset_item_id": self.id,
3366            "run_name": run_name,
3367            "dataset_id": self.dataset_id,
3368        }
3369        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3370
3371        self.link(
3372            trace, run_name, run_metadata=run_metadata, run_description=run_description
3373        )
3374
3375        return trace.get_langchain_handler(update_parent=True)
3376
3377    @contextmanager
3378    def observe(
3379        self,
3380        *,
3381        run_name: str,
3382        run_description: Optional[str] = None,
3383        run_metadata: Optional[Any] = None,
3384        trace_id: Optional[str] = None,
3385    ):
3386        """Observes a dataset run within the Langfuse client.
3387
3388        Args:
3389            run_name (str): The name of the dataset run.
3390            root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created.
3391            run_description (Optional[str]): The description of the dataset run.
3392            run_metadata (Optional[Any]): Additional metadata for the dataset run.
3393
3394        Yields:
3395            StatefulTraceClient: The trace associated with the dataset run.
3396        """
3397        from langfuse.decorators import langfuse_context
3398
3399        root_trace_id = trace_id or str(uuid.uuid4())
3400
3401        langfuse_context._set_root_trace_id(root_trace_id)
3402
3403        try:
3404            yield root_trace_id
3405
3406        finally:
3407            self.link(
3408                run_name=run_name,
3409                run_metadata=run_metadata,
3410                run_description=run_description,
3411                trace_or_observation=None,
3412                trace_id=root_trace_id,
3413            )
3414
3415    @contextmanager
3416    def observe_llama_index(
3417        self,
3418        *,
3419        run_name: str,
3420        run_description: Optional[str] = None,
3421        run_metadata: Optional[Any] = None,
3422        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3423    ):
3424        """Context manager for observing LlamaIndex operations linked to this dataset item.
3425
3426        This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging
3427        and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all
3428        operations performed within the context are linked to the appropriate dataset item and run in Langfuse.
3429
3430        Args:
3431            run_name (str): The name of the dataset run.
3432            run_description (Optional[str]): Description of the dataset run. Defaults to None.
3433            run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
3434            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass
3435                to the LlamaIndex integration constructor. Defaults to an empty dictionary.
3436
3437        Yields:
3438            LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.
3439
3440        Example:
3441            ```python
3442            dataset_item = dataset.items[0]
3443
3444            with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler:
3445                # Perform LlamaIndex operations here
3446                some_llama_index_operation()
3447            ```
3448
3449        Raises:
3450            ImportError: If required modules for LlamaIndex integration are not available.
3451        """
3452        metadata = {
3453            "dataset_item_id": self.id,
3454            "run_name": run_name,
3455            "dataset_id": self.dataset_id,
3456        }
3457        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3458        self.link(
3459            trace, run_name, run_metadata=run_metadata, run_description=run_description
3460        )
3461
3462        try:
3463            import llama_index.core
3464            from llama_index.core import Settings
3465            from llama_index.core.callbacks import CallbackManager
3466
3467            from langfuse.llama_index import LlamaIndexCallbackHandler
3468
3469            callback_handler = LlamaIndexCallbackHandler(
3470                **llama_index_integration_constructor_kwargs,
3471            )
3472            callback_handler.set_root(trace, update_root=True)
3473
3474            # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler
3475            # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it
3476            prev_global_handler = llama_index.core.global_handler
3477            prev_langfuse_handler = None
3478
3479            if isinstance(prev_global_handler, LlamaIndexCallbackHandler):
3480                llama_index.core.global_handler = None
3481
3482            if Settings.callback_manager is None:
3483                Settings.callback_manager = CallbackManager([callback_handler])
3484            else:
3485                for handler in Settings.callback_manager.handlers:
3486                    if isinstance(handler, LlamaIndexCallbackHandler):
3487                        prev_langfuse_handler = handler
3488                        Settings.callback_manager.remove_handler(handler)
3489
3490                Settings.callback_manager.add_handler(callback_handler)
3491
3492        except Exception as e:
3493            self.log.exception(e)
3494
3495        try:
3496            yield callback_handler
3497        finally:
3498            # Reset the handlers
3499            Settings.callback_manager.remove_handler(callback_handler)
3500            if prev_langfuse_handler is not None:
3501                Settings.callback_manager.add_handler(prev_langfuse_handler)
3502
3503            llama_index.core.global_handler = prev_global_handler
3504
3505    def get_llama_index_handler(
3506        self,
3507        *,
3508        run_name: str,
3509        run_description: Optional[str] = None,
3510        run_metadata: Optional[Any] = None,
3511        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3512    ):
3513        """Create and get a llama-index callback handler linked to this dataset item.
3514
3515        Args:
3516            run_name (str): The name of the dataset run to be used in the callback handler.
3517            run_description (Optional[str]): Description of the dataset run.
3518            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3519            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
3520
3521        Returns:
3522            LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.
3523        """
3524        metadata = {
3525            "dataset_item_id": self.id,
3526            "run_name": run_name,
3527            "dataset_id": self.dataset_id,
3528        }
3529        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3530
3531        self.link(
3532            trace, run_name, run_metadata=run_metadata, run_description=run_description
3533        )
3534
3535        try:
3536            from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler
3537
3538            callback_handler = LlamaIndexCallbackHandler(
3539                **llama_index_integration_constructor_kwargs,
3540            )
3541            callback_handler.set_root(trace, update_root=True)
3542
3543            return callback_handler
3544        except Exception as e:
3545            self.log.exception(e)

Class for managing dataset items in Langfuse.

Arguments:
  • id (str): Unique identifier of the dataset item.
  • status (DatasetStatus): The status of the dataset item. Can be either 'ACTIVE' or 'ARCHIVED'.
  • input (Any): Input data of the dataset item.
  • expected_output (Optional[Any]): Expected output of the dataset item.
  • metadata (Optional[Any]): Additional metadata of the dataset item.
  • source_trace_id (Optional[str]): Identifier of the source trace.
  • source_observation_id (Optional[str]): Identifier of the source observation.
  • dataset_id (str): Identifier of the dataset to which this item belongs.
  • dataset_name (str): Name of the dataset to which this item belongs.
  • created_at (datetime): Timestamp of dataset item creation.
  • updated_at (datetime): Timestamp of the last update to the dataset item.
  • langfuse (Langfuse): Instance of Langfuse client for API interactions.
Example:
from langfuse import Langfuse

langfuse = Langfuse()

dataset = langfuse.get_dataset("<dataset_name>")

for item in dataset.items:
    # Generate a completion using the input of every item
    completion, generation = llm_app.run(item.input)

    # Evaluate the completion
    generation.score(
        name="example-score",
        value=1
    )
DatasetItemClient( dataset_item: langfuse.api.DatasetItem, langfuse: Langfuse)
3259    def __init__(self, dataset_item: DatasetItem, langfuse: Langfuse):
3260        """Initialize the DatasetItemClient."""
3261        self.id = dataset_item.id
3262        self.status = dataset_item.status
3263        self.input = dataset_item.input
3264        self.expected_output = dataset_item.expected_output
3265        self.metadata = dataset_item.metadata
3266        self.source_trace_id = dataset_item.source_trace_id
3267        self.source_observation_id = dataset_item.source_observation_id
3268        self.dataset_id = dataset_item.dataset_id
3269        self.dataset_name = dataset_item.dataset_name
3270        self.created_at = dataset_item.created_at
3271        self.updated_at = dataset_item.updated_at
3272
3273        self.langfuse = langfuse

Initialize the DatasetItemClient.

log = <Logger langfuse (WARNING)>
id: str
input: Any
expected_output: Optional[Any]
metadata: Optional[Any]
source_trace_id: Optional[str]
source_observation_id: Optional[str]
dataset_id: str
dataset_name: str
created_at: datetime.datetime
updated_at: datetime.datetime
langfuse: Langfuse
def flush(self, observation: StatefulClient, run_name: str):
3275    def flush(self, observation: StatefulClient, run_name: str):
3276        """Flushes an observations task manager's queue.
3277
3278        Used before creating a dataset run item to ensure all events are persistent.
3279
3280        Args:
3281            observation (StatefulClient): The observation or trace client associated with the dataset item.
3282            run_name (str): The name of the dataset run.
3283        """
3284        observation.task_manager.flush()

Flushes an observations task manager's queue.

Used before creating a dataset run item to ensure all events are persistent.

Arguments:
  • observation (StatefulClient): The observation or trace client associated with the dataset item.
  • run_name (str): The name of the dataset run.
def get_langchain_handler( self, *, run_name: str, run_description: Optional[str] = None, run_metadata: Optional[Any] = None):
3347    def get_langchain_handler(
3348        self,
3349        *,
3350        run_name: str,
3351        run_description: Optional[str] = None,
3352        run_metadata: Optional[Any] = None,
3353    ):
3354        """Create and get a langchain callback handler linked to this dataset item.
3355
3356        Args:
3357            run_name (str): The name of the dataset run to be used in the callback handler.
3358            run_description (Optional[str]): Description of the dataset run.
3359            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3360
3361        Returns:
3362            CallbackHandler: An instance of CallbackHandler linked to the dataset item.
3363        """
3364        metadata = {
3365            "dataset_item_id": self.id,
3366            "run_name": run_name,
3367            "dataset_id": self.dataset_id,
3368        }
3369        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3370
3371        self.link(
3372            trace, run_name, run_metadata=run_metadata, run_description=run_description
3373        )
3374
3375        return trace.get_langchain_handler(update_parent=True)

Create and get a langchain callback handler linked to this dataset item.

Arguments:
  • run_name (str): The name of the dataset run to be used in the callback handler.
  • run_description (Optional[str]): Description of the dataset run.
  • run_metadata (Optional[Any]): Additional metadata to include in dataset run.
Returns:

CallbackHandler: An instance of CallbackHandler linked to the dataset item.

@contextmanager
def observe( self, *, run_name: str, run_description: Optional[str] = None, run_metadata: Optional[Any] = None, trace_id: Optional[str] = None):
3377    @contextmanager
3378    def observe(
3379        self,
3380        *,
3381        run_name: str,
3382        run_description: Optional[str] = None,
3383        run_metadata: Optional[Any] = None,
3384        trace_id: Optional[str] = None,
3385    ):
3386        """Observes a dataset run within the Langfuse client.
3387
3388        Args:
3389            run_name (str): The name of the dataset run.
3390            root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created.
3391            run_description (Optional[str]): The description of the dataset run.
3392            run_metadata (Optional[Any]): Additional metadata for the dataset run.
3393
3394        Yields:
3395            StatefulTraceClient: The trace associated with the dataset run.
3396        """
3397        from langfuse.decorators import langfuse_context
3398
3399        root_trace_id = trace_id or str(uuid.uuid4())
3400
3401        langfuse_context._set_root_trace_id(root_trace_id)
3402
3403        try:
3404            yield root_trace_id
3405
3406        finally:
3407            self.link(
3408                run_name=run_name,
3409                run_metadata=run_metadata,
3410                run_description=run_description,
3411                trace_or_observation=None,
3412                trace_id=root_trace_id,
3413            )

Observes a dataset run within the Langfuse client.

Arguments:
  • run_name (str): The name of the dataset run.
  • root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created.
  • run_description (Optional[str]): The description of the dataset run.
  • run_metadata (Optional[Any]): Additional metadata for the dataset run.
Yields:

StatefulTraceClient: The trace associated with the dataset run.

@contextmanager
def observe_llama_index( self, *, run_name: str, run_description: Optional[str] = None, run_metadata: Optional[Any] = None, llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {}):
3415    @contextmanager
3416    def observe_llama_index(
3417        self,
3418        *,
3419        run_name: str,
3420        run_description: Optional[str] = None,
3421        run_metadata: Optional[Any] = None,
3422        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3423    ):
3424        """Context manager for observing LlamaIndex operations linked to this dataset item.
3425
3426        This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging
3427        and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all
3428        operations performed within the context are linked to the appropriate dataset item and run in Langfuse.
3429
3430        Args:
3431            run_name (str): The name of the dataset run.
3432            run_description (Optional[str]): Description of the dataset run. Defaults to None.
3433            run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
3434            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass
3435                to the LlamaIndex integration constructor. Defaults to an empty dictionary.
3436
3437        Yields:
3438            LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.
3439
3440        Example:
3441            ```python
3442            dataset_item = dataset.items[0]
3443
3444            with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler:
3445                # Perform LlamaIndex operations here
3446                some_llama_index_operation()
3447            ```
3448
3449        Raises:
3450            ImportError: If required modules for LlamaIndex integration are not available.
3451        """
3452        metadata = {
3453            "dataset_item_id": self.id,
3454            "run_name": run_name,
3455            "dataset_id": self.dataset_id,
3456        }
3457        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3458        self.link(
3459            trace, run_name, run_metadata=run_metadata, run_description=run_description
3460        )
3461
3462        try:
3463            import llama_index.core
3464            from llama_index.core import Settings
3465            from llama_index.core.callbacks import CallbackManager
3466
3467            from langfuse.llama_index import LlamaIndexCallbackHandler
3468
3469            callback_handler = LlamaIndexCallbackHandler(
3470                **llama_index_integration_constructor_kwargs,
3471            )
3472            callback_handler.set_root(trace, update_root=True)
3473
3474            # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler
3475            # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it
3476            prev_global_handler = llama_index.core.global_handler
3477            prev_langfuse_handler = None
3478
3479            if isinstance(prev_global_handler, LlamaIndexCallbackHandler):
3480                llama_index.core.global_handler = None
3481
3482            if Settings.callback_manager is None:
3483                Settings.callback_manager = CallbackManager([callback_handler])
3484            else:
3485                for handler in Settings.callback_manager.handlers:
3486                    if isinstance(handler, LlamaIndexCallbackHandler):
3487                        prev_langfuse_handler = handler
3488                        Settings.callback_manager.remove_handler(handler)
3489
3490                Settings.callback_manager.add_handler(callback_handler)
3491
3492        except Exception as e:
3493            self.log.exception(e)
3494
3495        try:
3496            yield callback_handler
3497        finally:
3498            # Reset the handlers
3499            Settings.callback_manager.remove_handler(callback_handler)
3500            if prev_langfuse_handler is not None:
3501                Settings.callback_manager.add_handler(prev_langfuse_handler)
3502
3503            llama_index.core.global_handler = prev_global_handler

Context manager for observing LlamaIndex operations linked to this dataset item.

This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all operations performed within the context are linked to the appropriate dataset item and run in Langfuse.

Arguments:
  • run_name (str): The name of the dataset run.
  • run_description (Optional[str]): Description of the dataset run. Defaults to None.
  • run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
  • llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass to the LlamaIndex integration constructor. Defaults to an empty dictionary.
Yields:

LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.

Example:
dataset_item = dataset.items[0]

with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler:
    # Perform LlamaIndex operations here
    some_llama_index_operation()
Raises:
  • ImportError: If required modules for LlamaIndex integration are not available.
def get_llama_index_handler( self, *, run_name: str, run_description: Optional[str] = None, run_metadata: Optional[Any] = None, llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {}):
3505    def get_llama_index_handler(
3506        self,
3507        *,
3508        run_name: str,
3509        run_description: Optional[str] = None,
3510        run_metadata: Optional[Any] = None,
3511        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3512    ):
3513        """Create and get a llama-index callback handler linked to this dataset item.
3514
3515        Args:
3516            run_name (str): The name of the dataset run to be used in the callback handler.
3517            run_description (Optional[str]): Description of the dataset run.
3518            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3519            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
3520
3521        Returns:
3522            LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.
3523        """
3524        metadata = {
3525            "dataset_item_id": self.id,
3526            "run_name": run_name,
3527            "dataset_id": self.dataset_id,
3528        }
3529        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3530
3531        self.link(
3532            trace, run_name, run_metadata=run_metadata, run_description=run_description
3533        )
3534
3535        try:
3536            from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler
3537
3538            callback_handler = LlamaIndexCallbackHandler(
3539                **llama_index_integration_constructor_kwargs,
3540            )
3541            callback_handler.set_root(trace, update_root=True)
3542
3543            return callback_handler
3544        except Exception as e:
3545            self.log.exception(e)

Create and get a llama-index callback handler linked to this dataset item.

Arguments:
  • run_name (str): The name of the dataset run to be used in the callback handler.
  • run_description (Optional[str]): Description of the dataset run.
  • run_metadata (Optional[Any]): Additional metadata to include in dataset run.
  • llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
Returns:

LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.

class DatasetClient:
3548class DatasetClient:
3549    """Class for managing datasets in Langfuse.
3550
3551    Attributes:
3552        id (str): Unique identifier of the dataset.
3553        name (str): Name of the dataset.
3554        description (Optional[str]): Description of the dataset.
3555        metadata (Optional[typing.Any]): Additional metadata of the dataset.
3556        project_id (str): Identifier of the project to which the dataset belongs.
3557        dataset_name (str): Name of the dataset.
3558        created_at (datetime): Timestamp of dataset creation.
3559        updated_at (datetime): Timestamp of the last update to the dataset.
3560        items (List[DatasetItemClient]): List of dataset items associated with the dataset.
3561        runs (List[str]): List of dataset runs associated with the dataset. Deprecated.
3562
3563    Example:
3564        Print the input of each dataset item in a dataset.
3565        ```python
3566        from langfuse import Langfuse
3567
3568        langfuse = Langfuse()
3569
3570        dataset = langfuse.get_dataset("<dataset_name>")
3571
3572        for item in dataset.items:
3573            print(item.input)
3574        ```
3575    """
3576
3577    id: str
3578    name: str
3579    description: Optional[str]
3580    project_id: str
3581    dataset_name: str  # for backward compatibility, to be deprecated
3582    metadata: Optional[Any]
3583    created_at: dt.datetime
3584    updated_at: dt.datetime
3585    items: typing.List[DatasetItemClient]
3586    runs: typing.List[str] = []  # deprecated
3587
3588    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3589        """Initialize the DatasetClient."""
3590        self.id = dataset.id
3591        self.name = dataset.name
3592        self.description = dataset.description
3593        self.project_id = dataset.project_id
3594        self.metadata = dataset.metadata
3595        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3596        self.created_at = dataset.created_at
3597        self.updated_at = dataset.updated_at
3598        self.items = items

Class for managing datasets in Langfuse.

Attributes:
  • id (str): Unique identifier of the dataset.
  • name (str): Name of the dataset.
  • description (Optional[str]): Description of the dataset.
  • metadata (Optional[typing.Any]): Additional metadata of the dataset.
  • project_id (str): Identifier of the project to which the dataset belongs.
  • dataset_name (str): Name of the dataset.
  • created_at (datetime): Timestamp of dataset creation.
  • updated_at (datetime): Timestamp of the last update to the dataset.
  • items (List[DatasetItemClient]): List of dataset items associated with the dataset.
  • runs (List[str]): List of dataset runs associated with the dataset. Deprecated.
Example:

Print the input of each dataset item in a dataset.

from langfuse import Langfuse

langfuse = Langfuse()

dataset = langfuse.get_dataset("<dataset_name>")

for item in dataset.items:
    print(item.input)
DatasetClient( dataset: langfuse.api.Dataset, items: List[DatasetItemClient])
3588    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3589        """Initialize the DatasetClient."""
3590        self.id = dataset.id
3591        self.name = dataset.name
3592        self.description = dataset.description
3593        self.project_id = dataset.project_id
3594        self.metadata = dataset.metadata
3595        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3596        self.created_at = dataset.created_at
3597        self.updated_at = dataset.updated_at
3598        self.items = items

Initialize the DatasetClient.

id: str
name: str
description: Optional[str]
project_id: str
dataset_name: str
metadata: Optional[Any]
created_at: datetime.datetime
updated_at: datetime.datetime
items: List[DatasetItemClient]
runs: List[str] = []