langfuse.client

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

Response object for fetch_trace method.

FetchTraceResponse( data: langfuse.api.TraceWithFullDetails)
@dataclass
class FetchObservationsResponse:
106@dataclass
107class FetchObservationsResponse:
108    """Response object for fetch_observations method."""
109
110    data: typing.List[ObservationsView]
111    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:
114@dataclass
115class FetchObservationResponse:
116    """Response object for fetch_observation method."""
117
118    data: Observation

Response object for fetch_observation method.

FetchObservationResponse(data: langfuse.api.Observation)
@dataclass
class FetchMediaResponse:
121@dataclass
122class FetchMediaResponse:
123    """Response object for fetch_media method."""
124
125    data: GetMediaResponse

Response object for fetch_media method.

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

Get the current trace id.

def get_trace_url(self) -> str:
374    def get_trace_url(self) -> str:
375        """Get the URL of the current trace to view it in the Langfuse UI."""
376        project_id = self._get_project_id()
377        if not project_id:
378            return f"{self.base_url}/trace/{self.trace_id}"
379
380        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:
382    def get_dataset(
383        self, name: str, *, fetch_items_page_size: Optional[int] = 50
384    ) -> "DatasetClient":
385        """Fetch a dataset by its name.
386
387        Args:
388            name (str): The name of the dataset to fetch.
389            fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
390
391        Returns:
392            DatasetClient: The dataset with the given name.
393        """
394        try:
395            self.log.debug(f"Getting datasets {name}")
396            dataset = self.client.datasets.get(dataset_name=name)
397
398            dataset_items = []
399            page = 1
400            while True:
401                new_items = self.client.dataset_items.list(
402                    dataset_name=self._url_encode(name),
403                    page=page,
404                    limit=fetch_items_page_size,
405                )
406                dataset_items.extend(new_items.data)
407                if new_items.meta.total_pages <= page:
408                    break
409                page += 1
410
411            items = [DatasetItemClient(i, langfuse=self) for i in dataset_items]
412
413            return DatasetClient(dataset, items=items)
414        except Exception as e:
415            handle_fern_exception(e)
416            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:
418    def get_dataset_item(self, id: str) -> "DatasetItemClient":
419        """Get the dataset item with the given id."""
420        try:
421            self.log.debug(f"Getting dataset item {id}")
422            dataset_item = self.client.dataset_items.get(id=id)
423            return DatasetItemClient(dataset_item, langfuse=self)
424        except Exception as e:
425            handle_fern_exception(e)
426            raise e

Get the dataset item with the given id.

def auth_check(self) -> bool:
428    def auth_check(self) -> bool:
429        """Check if the provided credentials (public and secret key) are valid.
430
431        Raises:
432            Exception: If no projects were found for the provided credentials.
433
434        Note:
435            This method is blocking. It is discouraged to use it in production code.
436        """
437        try:
438            projects = self.client.projects.get()
439            self.log.debug(
440                f"Auth check successful, found {len(projects.data)} projects"
441            )
442            if len(projects.data) == 0:
443                raise Exception(
444                    "Auth check failed, no project found for the keys provided."
445                )
446            return True
447
448        except Exception as e:
449            handle_fern_exception(e)
450            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:
452    def get_dataset_runs(
453        self,
454        dataset_name: str,
455        *,
456        page: typing.Optional[int] = None,
457        limit: typing.Optional[int] = None,
458    ) -> PaginatedDatasetRuns:
459        """Get all dataset runs.
460
461        Args:
462            dataset_name (str): Name of the dataset.
463            page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None.
464            limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50.
465
466        Returns:
467            PaginatedDatasetRuns: The dataset runs.
468        """
469        try:
470            self.log.debug("Getting dataset runs")
471            return self.client.datasets.get_runs(
472                dataset_name=self._url_encode(dataset_name), page=page, limit=limit
473            )
474        except Exception as e:
475            handle_fern_exception(e)
476            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:
478    def get_dataset_run(
479        self,
480        dataset_name: str,
481        dataset_run_name: str,
482    ) -> DatasetRunWithItems:
483        """Get a dataset run.
484
485        Args:
486            dataset_name: Name of the dataset.
487            dataset_run_name: Name of the dataset run.
488
489        Returns:
490            DatasetRunWithItems: The dataset run.
491        """
492        try:
493            self.log.debug(
494                f"Getting dataset runs for dataset {dataset_name} and run {dataset_run_name}"
495            )
496            return self.client.datasets.get_run(
497                dataset_name=self._url_encode(dataset_name),
498                run_name=self._url_encode(dataset_run_name),
499            )
500        except Exception as e:
501            handle_fern_exception(e)
502            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:
504    def create_dataset(
505        self,
506        name: str,
507        description: Optional[str] = None,
508        metadata: Optional[Any] = None,
509    ) -> Dataset:
510        """Create a dataset with the given name on Langfuse.
511
512        Args:
513            name: Name of the dataset to create.
514            description: Description of the dataset. Defaults to None.
515            metadata: Additional metadata. Defaults to None.
516
517        Returns:
518            Dataset: The created dataset as returned by the Langfuse API.
519        """
520        try:
521            body = CreateDatasetRequest(
522                name=name, description=description, metadata=metadata
523            )
524            self.log.debug(f"Creating datasets {body}")
525            return self.client.datasets.create(request=body)
526        except Exception as e:
527            handle_fern_exception(e)
528            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:
530    def create_dataset_item(
531        self,
532        dataset_name: str,
533        input: Optional[Any] = None,
534        expected_output: Optional[Any] = None,
535        metadata: Optional[Any] = None,
536        source_trace_id: Optional[str] = None,
537        source_observation_id: Optional[str] = None,
538        status: Optional[DatasetStatus] = None,
539        id: Optional[str] = None,
540    ) -> DatasetItem:
541        """Create a dataset item.
542
543        Upserts if an item with id already exists.
544
545        Args:
546            dataset_name: Name of the dataset in which the dataset item should be created.
547            input: Input data. Defaults to None. Can contain any dict, list or scalar.
548            expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
549            metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
550            source_trace_id: Id of the source trace. Defaults to None.
551            source_observation_id: Id of the source observation. Defaults to None.
552            status: Status of the dataset item. Defaults to ACTIVE for newly created items.
553            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.
554
555        Returns:
556            DatasetItem: The created dataset item as returned by the Langfuse API.
557
558        Example:
559            ```python
560            from langfuse import Langfuse
561
562            langfuse = Langfuse()
563
564            # Uploading items to the Langfuse dataset named "capital_cities"
565            langfuse.create_dataset_item(
566                dataset_name="capital_cities",
567                input={"input": {"country": "Italy"}},
568                expected_output={"expected_output": "Rome"},
569                metadata={"foo": "bar"}
570            )
571            ```
572        """
573        try:
574            body = CreateDatasetItemRequest(
575                datasetName=dataset_name,
576                input=input,
577                expectedOutput=expected_output,
578                metadata=metadata,
579                sourceTraceId=source_trace_id,
580                sourceObservationId=source_observation_id,
581                status=status,
582                id=id,
583            )
584            self.log.debug(f"Creating dataset item {body}")
585            return self.client.dataset_items.create(request=body)
586        except Exception as e:
587            handle_fern_exception(e)
588            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:
590    def fetch_trace(
591        self,
592        id: str,
593    ) -> FetchTraceResponse:
594        """Fetch a trace via the Langfuse API by its id.
595
596        Args:
597            id: The id of the trace to fetch.
598
599        Returns:
600            FetchTraceResponse: The trace with full details as returned by the Langfuse API on `data`.
601
602        Raises:
603            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
604        """
605        try:
606            self.log.debug(f"Getting trace {id}")
607            trace = self.client.trace.get(id)
608            return FetchTraceResponse(data=trace)
609        except Exception as e:
610            handle_fern_exception(e)
611            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:
613    def get_trace(
614        self,
615        id: str,
616    ) -> TraceWithFullDetails:
617        """Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead.
618
619        Args:
620            id: The id of the trace to fetch.
621
622        Returns:
623            TraceWithFullDetails: The trace with full details as returned by the Langfuse API.
624
625        Raises:
626            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
627        """
628        warnings.warn(
629            "get_trace is deprecated, use fetch_trace instead.",
630            DeprecationWarning,
631        )
632
633        try:
634            self.log.debug(f"Getting trace {id}")
635            return self.client.trace.get(id)
636        except Exception as e:
637            handle_fern_exception(e)
638            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:
640    def fetch_traces(
641        self,
642        *,
643        page: Optional[int] = None,
644        limit: Optional[int] = None,
645        user_id: Optional[str] = None,
646        name: Optional[str] = None,
647        session_id: Optional[str] = None,
648        from_timestamp: Optional[dt.datetime] = None,
649        to_timestamp: Optional[dt.datetime] = None,
650        order_by: Optional[str] = None,
651        tags: Optional[Union[str, Sequence[str]]] = None,
652    ) -> FetchTracesResponse:
653        """Fetch a list of traces in the current project matching the given parameters.
654
655        Args:
656            page (Optional[int]): Page number, starts at 1. Defaults to None.
657            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.
658            name (Optional[str]): Filter by name of traces. Defaults to None.
659            user_id (Optional[str]): Filter by user_id. Defaults to None.
660            session_id (Optional[str]): Filter by session_id. Defaults to None.
661            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
662            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
663            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.
664            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
665
666        Returns:
667            FetchTracesResponse, list of traces on `data` and metadata on `meta`.
668
669        Raises:
670            Exception: If an error occurred during the request.
671        """
672        try:
673            self.log.debug(
674                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
675            )
676            res = self.client.trace.list(
677                page=page,
678                limit=limit,
679                name=name,
680                user_id=user_id,
681                session_id=session_id,
682                from_timestamp=from_timestamp,
683                to_timestamp=to_timestamp,
684                order_by=order_by,
685                tags=tags,
686            )
687            return FetchTracesResponse(data=res.data, meta=res.meta)
688        except Exception as e:
689            handle_fern_exception(e)
690            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:
692    def get_traces(
693        self,
694        *,
695        page: Optional[int] = None,
696        limit: Optional[int] = None,
697        user_id: Optional[str] = None,
698        name: Optional[str] = None,
699        session_id: Optional[str] = None,
700        from_timestamp: Optional[dt.datetime] = None,
701        to_timestamp: Optional[dt.datetime] = None,
702        order_by: Optional[str] = None,
703        tags: Optional[Union[str, Sequence[str]]] = None,
704    ) -> Traces:
705        """Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead.
706
707        Args:
708            page (Optional[int]): Page number, starts at 1. Defaults to None.
709            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.
710            name (Optional[str]): Filter by name of traces. Defaults to None.
711            user_id (Optional[str]): Filter by user_id. Defaults to None.
712            session_id (Optional[str]): Filter by session_id. Defaults to None.
713            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
714            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
715            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.
716            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
717
718        Returns:
719            List of Traces
720
721        Raises:
722            Exception: If an error occurred during the request.
723        """
724        warnings.warn(
725            "get_traces is deprecated, use fetch_traces instead.",
726            DeprecationWarning,
727        )
728        try:
729            self.log.debug(
730                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
731            )
732            return self.client.trace.list(
733                page=page,
734                limit=limit,
735                name=name,
736                user_id=user_id,
737                session_id=session_id,
738                from_timestamp=from_timestamp,
739                to_timestamp=to_timestamp,
740                order_by=order_by,
741                tags=tags,
742            )
743        except Exception as e:
744            handle_fern_exception(e)
745            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:
747    def fetch_observations(
748        self,
749        *,
750        page: typing.Optional[int] = None,
751        limit: typing.Optional[int] = None,
752        name: typing.Optional[str] = None,
753        user_id: typing.Optional[str] = None,
754        trace_id: typing.Optional[str] = None,
755        parent_observation_id: typing.Optional[str] = None,
756        from_start_time: typing.Optional[dt.datetime] = None,
757        to_start_time: typing.Optional[dt.datetime] = None,
758        type: typing.Optional[str] = None,
759    ) -> FetchObservationsResponse:
760        """Get a list of observations in the current project matching the given parameters.
761
762        Args:
763            page (Optional[int]): Page number of the observations to return. Defaults to None.
764            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
765            name (Optional[str]): Name of the observations to return. Defaults to None.
766            user_id (Optional[str]): User identifier. Defaults to None.
767            trace_id (Optional[str]): Trace identifier. Defaults to None.
768            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
769            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
770            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
771            type (Optional[str]): Type of the observation. Defaults to None.
772
773        Returns:
774            FetchObservationsResponse, list of observations on `data` and metadata on `meta`.
775
776        Raises:
777            Exception: If an error occurred during the request.
778        """
779        try:
780            self.log.debug(
781                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
782            )
783            res = self.client.observations.get_many(
784                page=page,
785                limit=limit,
786                name=name,
787                user_id=user_id,
788                trace_id=trace_id,
789                parent_observation_id=parent_observation_id,
790                from_start_time=from_start_time,
791                to_start_time=to_start_time,
792                type=type,
793            )
794            return FetchObservationsResponse(data=res.data, meta=res.meta)
795        except Exception as e:
796            self.log.exception(e)
797            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:
799    def get_observations(
800        self,
801        *,
802        page: typing.Optional[int] = None,
803        limit: typing.Optional[int] = None,
804        name: typing.Optional[str] = None,
805        user_id: typing.Optional[str] = None,
806        trace_id: typing.Optional[str] = None,
807        parent_observation_id: typing.Optional[str] = None,
808        from_start_time: typing.Optional[dt.datetime] = None,
809        to_start_time: typing.Optional[dt.datetime] = None,
810        type: typing.Optional[str] = None,
811    ) -> ObservationsViews:
812        """Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead.
813
814        Args:
815            page (Optional[int]): Page number of the observations to return. Defaults to None.
816            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
817            name (Optional[str]): Name of the observations to return. Defaults to None.
818            user_id (Optional[str]): User identifier. Defaults to None.
819            trace_id (Optional[str]): Trace identifier. Defaults to None.
820            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
821            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
822            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
823            type (Optional[str]): Type of the observation. Defaults to None.
824
825        Returns:
826            List of ObservationsViews: List of observations in the project matching the given parameters.
827
828        Raises:
829            Exception: If an error occurred during the request.
830        """
831        warnings.warn(
832            "get_observations is deprecated, use fetch_observations instead.",
833            DeprecationWarning,
834        )
835        try:
836            self.log.debug(
837                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
838            )
839            return self.client.observations.get_many(
840                page=page,
841                limit=limit,
842                name=name,
843                user_id=user_id,
844                trace_id=trace_id,
845                parent_observation_id=parent_observation_id,
846                from_start_time=from_start_time,
847                to_start_time=to_start_time,
848                type=type,
849            )
850        except Exception as e:
851            handle_fern_exception(e)
852            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:
854    def get_generations(
855        self,
856        *,
857        page: typing.Optional[int] = None,
858        limit: typing.Optional[int] = None,
859        name: typing.Optional[str] = None,
860        user_id: typing.Optional[str] = None,
861        trace_id: typing.Optional[str] = None,
862        from_start_time: typing.Optional[dt.datetime] = None,
863        to_start_time: typing.Optional[dt.datetime] = None,
864        parent_observation_id: typing.Optional[str] = None,
865    ) -> ObservationsViews:
866        """Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead.
867
868        Args:
869            page (Optional[int]): Page number of the generations to return. Defaults to None.
870            limit (Optional[int]): Maximum number of generations to return. Defaults to None.
871            name (Optional[str]): Name of the generations to return. Defaults to None.
872            user_id (Optional[str]): User identifier of the generations to return. Defaults to None.
873            trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None.
874            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
875            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
876            parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None.
877
878        Returns:
879            List of ObservationsViews: List of generations in the project matching the given parameters.
880
881        Raises:
882            Exception: If an error occurred during the request.
883        """
884        warnings.warn(
885            "get_generations is deprecated, use `fetch_observations(type='GENERATION')` instead.",
886            DeprecationWarning,
887        )
888        return self.get_observations(
889            page=page,
890            limit=limit,
891            name=name,
892            user_id=user_id,
893            trace_id=trace_id,
894            parent_observation_id=parent_observation_id,
895            from_start_time=from_start_time,
896            to_start_time=to_start_time,
897            type="GENERATION",
898        )

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:
900    def fetch_observation(
901        self,
902        id: str,
903    ) -> FetchObservationResponse:
904        """Get an observation in the current project with the given identifier.
905
906        Args:
907            id: The identifier of the observation to fetch.
908
909        Returns:
910            FetchObservationResponse: The observation with the given id on `data`.
911
912        Raises:
913            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
914        """
915        try:
916            self.log.debug(f"Getting observation {id}")
917            observation = self.client.observations.get(id)
918            return FetchObservationResponse(data=observation)
919        except Exception as e:
920            handle_fern_exception(e)
921            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:
923    def fetch_media(self, id: str) -> FetchMediaResponse:
924        """Get media content by ID.
925
926        Args:
927            id: The identifier of the media content to fetch.
928
929        Returns:
930            FetchMediaResponse: The media data of the given id on `data`.
931
932        Raises:
933            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.
934        """
935        try:
936            return FetchMediaResponse(data=self.client.media.get(id))
937        except Exception as e:
938            handle_fern_exception(e)
939            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):
941    def resolve_media_references(
942        self,
943        *,
944        obj: Any,
945        resolve_with: Literal["base64_data_uri"],
946        max_depth: int = 10,
947        content_fetch_timeout_seconds: int = 10,
948    ):
949        """Replace media reference strings in an object with base64 data URIs.
950
951        This method recursively traverses an object (up to max_depth) looking for media reference strings
952        in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using
953        the provided Langfuse client and replaces the reference string with a base64 data URI.
954
955        If fetching media content fails for a reference string, a warning is logged and the reference
956        string is left unchanged.
957
958        Args:
959            obj: The object to process. Can be a primitive value, array, or nested object.
960                If the object has a __dict__ attribute, a dict will be returned instead of the original object type.
961            resolve_with: The representation of the media content to replace the media reference string with.
962                Currently only "base64_data_uri" is supported.
963            max_depth: int: The maximum depth to traverse the object. Default is 10.
964            content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 10.
965
966        Returns:
967            A deep copy of the input object with all media references replaced with base64 data URIs where possible.
968            If the input object has a __dict__ attribute, a dict will be returned instead of the original object type.
969
970        Example:
971            obj = {
972                "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@",
973                "nested": {
974                    "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@"
975                }
976            }
977
978            result = await LangfuseMedia.resolve_media_references(obj, langfuse_client)
979
980            # Result:
981            # {
982            #     "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
983            #     "nested": {
984            #         "pdf": "data:application/pdf;base64,JVBERi0xLjcK..."
985            #     }
986            # }
987        """
988        return LangfuseMedia.resolve_media_references(
989            langfuse_client=self,
990            obj=obj,
991            resolve_with=resolve_with,
992            max_depth=max_depth,
993            content_fetch_timeout_seconds=content_fetch_timeout_seconds,
994        )

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:
 996    def get_observation(
 997        self,
 998        id: str,
 999    ) -> Observation:
1000        """Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead.
1001
1002        Args:
1003            id: The identifier of the observation to fetch.
1004
1005        Raises:
1006            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
1007        """
1008        warnings.warn(
1009            "get_observation is deprecated, use fetch_observation instead.",
1010            DeprecationWarning,
1011        )
1012        try:
1013            self.log.debug(f"Getting observation {id}")
1014            return self.client.observations.get(id)
1015        except Exception as e:
1016            handle_fern_exception(e)
1017            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:
1019    def fetch_sessions(
1020        self,
1021        *,
1022        page: typing.Optional[int] = None,
1023        limit: typing.Optional[int] = None,
1024        from_timestamp: typing.Optional[dt.datetime] = None,
1025        to_timestamp: typing.Optional[dt.datetime] = None,
1026    ) -> FetchSessionsResponse:
1027        """Get a list of sessions in the current project.
1028
1029        Args:
1030            page (Optional[int]): Page number of the sessions to return. Defaults to None.
1031            limit (Optional[int]): Maximum number of sessions to return. Defaults to None.
1032            from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None.
1033            to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None.
1034
1035        Returns:
1036            FetchSessionsResponse, list of sessions on `data` and metadata on `meta`.
1037
1038        Raises:
1039            Exception: If an error occurred during the request.
1040        """
1041        try:
1042            self.log.debug(
1043                f"Getting sessions... {page}, {limit}, {from_timestamp}, {to_timestamp}"
1044            )
1045            res = self.client.sessions.list(
1046                page=page,
1047                limit=limit,
1048                from_timestamp=from_timestamp,
1049                to_timestamp=to_timestamp,
1050            )
1051            return FetchSessionsResponse(data=res.data, meta=res.meta)
1052        except Exception as e:
1053            handle_fern_exception(e)
1054            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]:
1084    def get_prompt(
1085        self,
1086        name: str,
1087        version: Optional[int] = None,
1088        *,
1089        label: Optional[str] = None,
1090        type: Literal["chat", "text"] = "text",
1091        cache_ttl_seconds: Optional[int] = None,
1092        fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None,
1093        max_retries: Optional[int] = None,
1094        fetch_timeout_seconds: Optional[int] = None,
1095    ) -> PromptClient:
1096        """Get a prompt.
1097
1098        This method attempts to fetch the requested prompt from the local cache. If the prompt is not found
1099        in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again
1100        and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will
1101        return the expired prompt as a fallback.
1102
1103        Args:
1104            name (str): The name of the prompt to retrieve.
1105
1106        Keyword Args:
1107            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.
1108            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.
1109            cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a
1110            keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0.
1111            type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text".
1112            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.
1113            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.
1114            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.
1115
1116        Returns:
1117            The prompt object retrieved from the cache or directly fetched if not cached or expired of type
1118            - TextPromptClient, if type argument is 'text'.
1119            - ChatPromptClient, if type argument is 'chat'.
1120
1121        Raises:
1122            Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
1123            expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
1124        """
1125        if version is not None and label is not None:
1126            raise ValueError("Cannot specify both version and label at the same time.")
1127
1128        if not name:
1129            raise ValueError("Prompt name cannot be empty.")
1130
1131        cache_key = PromptCache.generate_cache_key(name, version=version, label=label)
1132        bounded_max_retries = self._get_bounded_max_retries(
1133            max_retries, default_max_retries=2, max_retries_upper_bound=4
1134        )
1135
1136        self.log.debug(f"Getting prompt '{cache_key}'")
1137        cached_prompt = self.prompt_cache.get(cache_key)
1138
1139        if cached_prompt is None or cache_ttl_seconds == 0:
1140            self.log.debug(
1141                f"Prompt '{cache_key}' not found in cache or caching disabled."
1142            )
1143            try:
1144                return self._fetch_prompt_and_update_cache(
1145                    name,
1146                    version=version,
1147                    label=label,
1148                    ttl_seconds=cache_ttl_seconds,
1149                    max_retries=bounded_max_retries,
1150                    fetch_timeout_seconds=fetch_timeout_seconds,
1151                )
1152            except Exception as e:
1153                if fallback:
1154                    self.log.warning(
1155                        f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}"
1156                    )
1157
1158                    fallback_client_args = {
1159                        "name": name,
1160                        "prompt": fallback,
1161                        "type": type,
1162                        "version": version or 0,
1163                        "config": {},
1164                        "labels": [label] if label else [],
1165                        "tags": [],
1166                    }
1167
1168                    if type == "text":
1169                        return TextPromptClient(
1170                            prompt=Prompt_Text(**fallback_client_args),
1171                            is_fallback=True,
1172                        )
1173
1174                    if type == "chat":
1175                        return ChatPromptClient(
1176                            prompt=Prompt_Chat(**fallback_client_args),
1177                            is_fallback=True,
1178                        )
1179
1180                raise e
1181
1182        if cached_prompt.is_expired():
1183            self.log.debug(f"Stale prompt '{cache_key}' found in cache.")
1184            try:
1185                # refresh prompt in background thread, refresh_prompt deduplicates tasks
1186                self.log.debug(f"Refreshing prompt '{cache_key}' in background.")
1187                self.prompt_cache.add_refresh_prompt_task(
1188                    cache_key,
1189                    lambda: self._fetch_prompt_and_update_cache(
1190                        name,
1191                        version=version,
1192                        label=label,
1193                        ttl_seconds=cache_ttl_seconds,
1194                        max_retries=bounded_max_retries,
1195                        fetch_timeout_seconds=fetch_timeout_seconds,
1196                    ),
1197                )
1198                self.log.debug(f"Returning stale prompt '{cache_key}' from cache.")
1199                # return stale prompt
1200                return cached_prompt.value
1201
1202            except Exception as e:
1203                self.log.warning(
1204                    f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}"
1205                )
1206                # creation of refresh prompt task failed, return stale prompt
1207                return cached_prompt.value
1208
1209        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]:
1303    def create_prompt(
1304        self,
1305        *,
1306        name: str,
1307        prompt: Union[str, List[ChatMessageDict]],
1308        is_active: Optional[bool] = None,  # deprecated
1309        labels: List[str] = [],
1310        tags: Optional[List[str]] = None,
1311        type: Optional[Literal["chat", "text"]] = "text",
1312        config: Optional[Any] = None,
1313        commit_message: Optional[str] = None,
1314    ) -> PromptClient:
1315        """Create a new prompt in Langfuse.
1316
1317        Keyword Args:
1318            name : The name of the prompt to be created.
1319            prompt : The content of the prompt to be created.
1320            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.
1321            labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label.
1322            tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt.
1323            config: Additional structured data to be saved with the prompt. Defaults to None.
1324            type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text".
1325            commit_message: Optional string describing the change.
1326
1327        Returns:
1328            TextPromptClient: The prompt if type argument is 'text'.
1329            ChatPromptClient: The prompt if type argument is 'chat'.
1330        """
1331        try:
1332            self.log.debug(f"Creating prompt {name=}, {version=}, {labels=}")
1333
1334            # Handle deprecated is_active flag
1335            if is_active:
1336                self.log.warning(
1337                    "The 'is_active' flag is deprecated and will be removed in a future release. Please use the 'production' label instead."
1338                )
1339
1340                labels = labels if "production" in labels else labels + ["production"]
1341
1342            if type == "chat":
1343                if not isinstance(prompt, list):
1344                    raise ValueError(
1345                        "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes."
1346                    )
1347                request = CreatePromptRequest_Chat(
1348                    name=name,
1349                    prompt=prompt,
1350                    labels=labels,
1351                    tags=tags,
1352                    config=config or {},
1353                    commitMessage=commit_message,
1354                    type="chat",
1355                )
1356                server_prompt = self.client.prompts.create(request=request)
1357
1358                return ChatPromptClient(prompt=server_prompt)
1359
1360            if not isinstance(prompt, str):
1361                raise ValueError("For 'text' type, 'prompt' must be a string.")
1362
1363            request = CreatePromptRequest_Text(
1364                name=name,
1365                prompt=prompt,
1366                labels=labels,
1367                tags=tags,
1368                config=config or {},
1369                commitMessage=commit_message,
1370                type="text",
1371            )
1372
1373            server_prompt = self.client.prompts.create(request=request)
1374            return TextPromptClient(prompt=server_prompt)
1375
1376        except Exception as e:
1377            handle_fern_exception(e)
1378            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] = []):
1380    def update_prompt(
1381        self,
1382        *,
1383        name: str,
1384        version: int,
1385        new_labels: List[str] = [],
1386    ):
1387        """Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.
1388
1389        Args:
1390            name (str): The name of the prompt to update.
1391            version (int): The version number of the prompt to update.
1392            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 [].
1393
1394        Returns:
1395            Prompt: The updated prompt from the Langfuse API.
1396
1397        """
1398        updated_prompt = self.client.prompt_version.update(
1399            name=name,
1400            version=version,
1401            new_labels=new_labels,
1402        )
1403        self.prompt_cache.invalidate(name)
1404        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:
1409    def trace(
1410        self,
1411        *,
1412        id: typing.Optional[str] = None,
1413        name: typing.Optional[str] = None,
1414        user_id: typing.Optional[str] = None,
1415        session_id: typing.Optional[str] = None,
1416        version: typing.Optional[str] = None,
1417        input: typing.Optional[typing.Any] = None,
1418        output: typing.Optional[typing.Any] = None,
1419        metadata: typing.Optional[typing.Any] = None,
1420        tags: typing.Optional[typing.List[str]] = None,
1421        timestamp: typing.Optional[dt.datetime] = None,
1422        public: typing.Optional[bool] = None,
1423        **kwargs,
1424    ) -> "StatefulTraceClient":
1425        """Create a trace.
1426
1427        Args:
1428            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.
1429            name: Identifier of the trace. Useful for sorting/filtering in the UI.
1430            input: The input of the trace. Can be any JSON object.
1431            output: The output of the trace. Can be any JSON object.
1432            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
1433            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
1434            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
1435            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
1436            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
1437            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.
1438            timestamp: The timestamp of the trace. Defaults to the current time if not provided.
1439            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.
1440            **kwargs: Additional keyword arguments that can be included in the trace.
1441
1442        Returns:
1443            StatefulTraceClient: The created trace.
1444
1445        Example:
1446            ```python
1447            from langfuse import Langfuse
1448
1449            langfuse = Langfuse()
1450
1451            trace = langfuse.trace(
1452                name="example-application",
1453                user_id="user-1234")
1454            )
1455            ```
1456        """
1457        new_id = id or str(uuid.uuid4())
1458        self.trace_id = new_id
1459        try:
1460            new_dict = {
1461                "id": new_id,
1462                "name": name,
1463                "userId": user_id,
1464                "sessionId": session_id
1465                or kwargs.get("sessionId", None),  # backward compatibility
1466                "release": self.release,
1467                "version": version,
1468                "metadata": metadata,
1469                "input": input,
1470                "output": output,
1471                "tags": tags,
1472                "timestamp": timestamp or _get_timestamp(),
1473                "public": public,
1474            }
1475            if kwargs is not None:
1476                new_dict.update(kwargs)
1477
1478            new_body = TraceBody(**new_dict)
1479
1480            self.log.debug(f"Creating trace {_filter_io_from_event_body(new_dict)}")
1481            event = {
1482                "id": str(uuid.uuid4()),
1483                "type": "trace-create",
1484                "body": new_body,
1485            }
1486
1487            self.task_manager.add_task(
1488                event,
1489            )
1490
1491        except Exception as e:
1492            self.log.exception(e)
1493        finally:
1494            self._log_memory_usage()
1495
1496            return StatefulTraceClient(
1497                self.client, new_id, StateType.TRACE, new_id, self.task_manager
1498            )

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:
1577    def score(
1578        self,
1579        *,
1580        name: str,
1581        value: typing.Union[float, str],
1582        data_type: typing.Optional[ScoreDataType] = None,
1583        trace_id: typing.Optional[str] = None,
1584        id: typing.Optional[str] = None,
1585        comment: typing.Optional[str] = None,
1586        observation_id: typing.Optional[str] = None,
1587        config_id: typing.Optional[str] = None,
1588        **kwargs,
1589    ) -> "StatefulClient":
1590        """Create a score attached to a trace (and optionally an observation).
1591
1592        Args:
1593            name (str): Identifier of the score.
1594            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.
1595            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.
1596              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.
1597            trace_id (str): The id of the trace to which the score should be attached.
1598            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
1599            comment (Optional[str]): Additional context/explanation of the score.
1600            observation_id (Optional[str]): The id of the observation to which the score should be attached.
1601            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
1602            **kwargs: Additional keyword arguments to include in the score.
1603
1604        Returns:
1605            StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided).
1606
1607        Example:
1608            ```python
1609            from langfuse import Langfuse
1610
1611            langfuse = Langfuse()
1612
1613            # Create a trace
1614            trace = langfuse.trace(name="example-application")
1615
1616            # Get id of created trace
1617            trace_id = trace.id
1618
1619            # Add score to the trace
1620            trace = langfuse.score(
1621                trace_id=trace_id,
1622                name="user-explicit-feedback",
1623                value=0.9,
1624                comment="I like how personalized the response is"
1625            )
1626            ```
1627        """
1628        trace_id = trace_id or self.trace_id or str(uuid.uuid4())
1629        new_id = id or str(uuid.uuid4())
1630        try:
1631            new_dict = {
1632                "id": new_id,
1633                "trace_id": trace_id,
1634                "observation_id": observation_id,
1635                "name": name,
1636                "value": value,
1637                "data_type": data_type,
1638                "comment": comment,
1639                "config_id": config_id,
1640                **kwargs,
1641            }
1642
1643            self.log.debug(f"Creating score {new_dict}...")
1644            new_body = ScoreBody(**new_dict)
1645
1646            event = {
1647                "id": str(uuid.uuid4()),
1648                "type": "score-create",
1649                "body": new_body,
1650            }
1651            self.task_manager.add_task(event)
1652
1653        except Exception as e:
1654            self.log.exception(e)
1655        finally:
1656            if observation_id is not None:
1657                return StatefulClient(
1658                    self.client,
1659                    observation_id,
1660                    StateType.OBSERVATION,
1661                    trace_id,
1662                    self.task_manager,
1663                )
1664            else:
1665                return StatefulClient(
1666                    self.client, new_id, StateType.TRACE, new_id, self.task_manager
1667                )

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:
1669    def span(
1670        self,
1671        *,
1672        id: typing.Optional[str] = None,
1673        trace_id: typing.Optional[str] = None,
1674        parent_observation_id: typing.Optional[str] = None,
1675        name: typing.Optional[str] = None,
1676        start_time: typing.Optional[dt.datetime] = None,
1677        end_time: typing.Optional[dt.datetime] = None,
1678        metadata: typing.Optional[typing.Any] = None,
1679        level: typing.Optional[SpanLevel] = None,
1680        status_message: typing.Optional[str] = None,
1681        input: typing.Optional[typing.Any] = None,
1682        output: typing.Optional[typing.Any] = None,
1683        version: typing.Optional[str] = None,
1684        **kwargs,
1685    ) -> "StatefulSpanClient":
1686        """Create a span.
1687
1688        A span represents durations of units of work in a trace.
1689        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.
1690
1691        If no trace_id is provided, a new trace is created just for this span.
1692
1693        Args:
1694            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
1695            trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated.
1696            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1697            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
1698            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
1699            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
1700            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
1701            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.
1702            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.
1703            input (Optional[dict]): The input to the span. Can be any JSON object.
1704            output (Optional[dict]): The output to the span. Can be any JSON object.
1705            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1706            **kwargs: Additional keyword arguments to include in the span.
1707
1708        Returns:
1709            StatefulSpanClient: The created span.
1710
1711        Example:
1712            ```python
1713            from langfuse import Langfuse
1714
1715            langfuse = Langfuse()
1716
1717            trace = langfuse.trace(name = "llm-feature")
1718
1719            # Create a span
1720            retrieval = langfuse.span(name = "retrieval", trace_id = trace.id)
1721
1722            # Create a nested span
1723            nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id)
1724            ```
1725        """
1726        new_span_id = id or str(uuid.uuid4())
1727        new_trace_id = trace_id or str(uuid.uuid4())
1728        self.trace_id = new_trace_id
1729        try:
1730            span_body = {
1731                "id": new_span_id,
1732                "trace_id": new_trace_id,
1733                "name": name,
1734                "start_time": start_time or _get_timestamp(),
1735                "metadata": metadata,
1736                "input": input,
1737                "output": output,
1738                "level": level,
1739                "status_message": status_message,
1740                "parent_observation_id": parent_observation_id,
1741                "version": version,
1742                "end_time": end_time,
1743                "trace": {"release": self.release},
1744                **kwargs,
1745            }
1746
1747            if trace_id is None:
1748                self._generate_trace(new_trace_id, name or new_trace_id)
1749
1750            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
1751
1752            span_body = CreateSpanBody(**span_body)
1753
1754            event = {
1755                "id": str(uuid.uuid4()),
1756                "type": "span-create",
1757                "body": span_body,
1758            }
1759
1760            self.task_manager.add_task(event)
1761
1762        except Exception as e:
1763            self.log.exception(e)
1764        finally:
1765            self._log_memory_usage()
1766
1767            return StatefulSpanClient(
1768                self.client,
1769                new_span_id,
1770                StateType.OBSERVATION,
1771                new_trace_id,
1772                self.task_manager,
1773            )

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:
1775    def event(
1776        self,
1777        *,
1778        id: typing.Optional[str] = None,
1779        trace_id: typing.Optional[str] = None,
1780        parent_observation_id: typing.Optional[str] = None,
1781        name: typing.Optional[str] = None,
1782        start_time: typing.Optional[dt.datetime] = None,
1783        metadata: typing.Optional[typing.Any] = None,
1784        input: typing.Optional[typing.Any] = None,
1785        output: typing.Optional[typing.Any] = None,
1786        level: typing.Optional[SpanLevel] = None,
1787        status_message: typing.Optional[str] = None,
1788        version: typing.Optional[str] = None,
1789        **kwargs,
1790    ) -> "StatefulSpanClient":
1791        """Create an event.
1792
1793        An event represents a discrete event in a trace.
1794        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.
1795
1796        If no trace_id is provided, a new trace is created just for this event.
1797
1798        Args:
1799            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
1800            trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event.
1801            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1802            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
1803            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
1804            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
1805            input (Optional[Any]): The input to the event. Can be any JSON object.
1806            output (Optional[Any]): The output to the event. Can be any JSON object.
1807            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.
1808            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.
1809            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
1810            **kwargs: Additional keyword arguments to include in the event.
1811
1812        Returns:
1813            StatefulSpanClient: The created event.
1814
1815        Example:
1816            ```python
1817            from langfuse import Langfuse
1818
1819            langfuse = Langfuse()
1820
1821            trace = langfuse.trace(name = "llm-feature")
1822
1823            # Create an event
1824            retrieval = langfuse.event(name = "retrieval", trace_id = trace.id)
1825            ```
1826        """
1827        event_id = id or str(uuid.uuid4())
1828        new_trace_id = trace_id or str(uuid.uuid4())
1829        self.trace_id = new_trace_id
1830        try:
1831            event_body = {
1832                "id": event_id,
1833                "trace_id": new_trace_id,
1834                "name": name,
1835                "start_time": start_time or _get_timestamp(),
1836                "metadata": metadata,
1837                "input": input,
1838                "output": output,
1839                "level": level,
1840                "status_message": status_message,
1841                "parent_observation_id": parent_observation_id,
1842                "version": version,
1843                "trace": {"release": self.release},
1844                **kwargs,
1845            }
1846
1847            if trace_id is None:
1848                self._generate_trace(new_trace_id, name or new_trace_id)
1849
1850            request = CreateEventBody(**event_body)
1851
1852            event = {
1853                "id": str(uuid.uuid4()),
1854                "type": "event-create",
1855                "body": request,
1856            }
1857
1858            self.log.debug(
1859                f"Creating event {_filter_io_from_event_body(event_body)} ..."
1860            )
1861            self.task_manager.add_task(event)
1862
1863        except Exception as e:
1864            self.log.exception(e)
1865        finally:
1866            return StatefulSpanClient(
1867                self.client,
1868                event_id,
1869                StateType.OBSERVATION,
1870                new_trace_id,
1871                self.task_manager,
1872            )

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:
1874    def generation(
1875        self,
1876        *,
1877        id: typing.Optional[str] = None,
1878        trace_id: typing.Optional[str] = None,
1879        parent_observation_id: typing.Optional[str] = None,
1880        name: typing.Optional[str] = None,
1881        start_time: typing.Optional[dt.datetime] = None,
1882        end_time: typing.Optional[dt.datetime] = None,
1883        completion_start_time: typing.Optional[dt.datetime] = None,
1884        metadata: typing.Optional[typing.Any] = None,
1885        level: typing.Optional[SpanLevel] = None,
1886        status_message: typing.Optional[str] = None,
1887        version: typing.Optional[str] = None,
1888        model: typing.Optional[str] = None,
1889        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
1890        input: typing.Optional[typing.Any] = None,
1891        output: typing.Optional[typing.Any] = None,
1892        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
1893        usage_details: typing.Optional[typing.Dict[str, int]] = None,
1894        cost_details: typing.Optional[typing.Dict[str, float]] = None,
1895        prompt: typing.Optional[PromptClient] = None,
1896        **kwargs,
1897    ) -> "StatefulGenerationClient":
1898        """Create a generation.
1899
1900        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.
1901
1902        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.
1903
1904        If no trace_id is provided, a new trace is created just for this generation.
1905
1906        Args:
1907            id (Optional[str]): The id of the generation can be set, defaults to random id.
1908            trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created
1909            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1910            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
1911            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
1912            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
1913            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.
1914            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
1915            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.
1916            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.
1917            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1918            model (Optional[str]): The name of the model used for the generation.
1919            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
1920            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
1921            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
1922            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.
1923            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}.
1924            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}.
1925            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
1926            **kwargs: Additional keyword arguments to include in the generation.
1927
1928        Returns:
1929            StatefulGenerationClient: The created generation.
1930
1931        Example:
1932            ```python
1933            from langfuse import Langfuse
1934
1935            langfuse = Langfuse()
1936
1937            # Create a generation in Langfuse
1938            generation = langfuse.generation(
1939                name="summary-generation",
1940                model="gpt-3.5-turbo",
1941                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
1942                input=[{"role": "system", "content": "You are a helpful assistant."},
1943                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
1944                metadata={"interface": "whatsapp"}
1945            )
1946            ```
1947        """
1948        new_trace_id = trace_id or str(uuid.uuid4())
1949        new_generation_id = id or str(uuid.uuid4())
1950        self.trace_id = new_trace_id
1951        try:
1952            generation_body = {
1953                "id": new_generation_id,
1954                "trace_id": new_trace_id,
1955                "release": self.release,
1956                "name": name,
1957                "start_time": start_time or _get_timestamp(),
1958                "metadata": metadata,
1959                "input": input,
1960                "output": output,
1961                "level": level,
1962                "status_message": status_message,
1963                "parent_observation_id": parent_observation_id,
1964                "version": version,
1965                "end_time": end_time,
1966                "completion_start_time": completion_start_time,
1967                "model": model,
1968                "model_parameters": model_parameters,
1969                "usage": _convert_usage_input(usage) if usage is not None else None,
1970                "usage_details": usage_details,
1971                "cost_details": cost_details,
1972                "trace": {"release": self.release},
1973                **_create_prompt_context(prompt),
1974                **kwargs,
1975            }
1976
1977            if trace_id is None:
1978                trace = {
1979                    "id": new_trace_id,
1980                    "release": self.release,
1981                    "name": name,
1982                }
1983                request = TraceBody(**trace)
1984
1985                event = {
1986                    "id": str(uuid.uuid4()),
1987                    "type": "trace-create",
1988                    "body": request,
1989                }
1990
1991                self.log.debug("Creating trace...")
1992
1993                self.task_manager.add_task(event)
1994
1995            self.log.debug(
1996                f"Creating generation max {_filter_io_from_event_body(generation_body)}..."
1997            )
1998            request = CreateGenerationBody(**generation_body)
1999
2000            event = {
2001                "id": str(uuid.uuid4()),
2002                "type": "generation-create",
2003                "body": request,
2004            }
2005
2006            self.task_manager.add_task(event)
2007
2008        except Exception as e:
2009            self.log.exception(e)
2010        finally:
2011            return StatefulGenerationClient(
2012                self.client,
2013                new_generation_id,
2014                StateType.OBSERVATION,
2015                new_trace_id,
2016                self.task_manager,
2017            )

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):
2037    def join(self):
2038        """Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter.
2039
2040        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.
2041        To guarantee all messages have been delivered, you still need to call flush().
2042        """
2043        try:
2044            return self.task_manager.join()
2045        except Exception as e:
2046            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):
2048    def flush(self):
2049        """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.
2050
2051        Example:
2052            ```python
2053            from langfuse import Langfuse
2054
2055            langfuse = Langfuse()
2056
2057            # Some operations with Langfuse
2058
2059            # Flushing all events to end Langfuse cleanly
2060            langfuse.flush()
2061            ```
2062        """
2063        try:
2064            return self.task_manager.flush()
2065        except Exception as e:
2066            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):
2068    def shutdown(self):
2069        """Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated.
2070
2071        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.
2072        As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API.
2073        """
2074        try:
2075            self.prompt_cache._task_manager.shutdown()
2076
2077            # In logging.py, a handler is attached to the httpx logger.
2078            # To avoid a memory leak on singleton reset, remove all handlers
2079            httpx_logger = logging.getLogger("httpx")
2080            for handler in httpx_logger.handlers:
2081                httpx_logger.removeHandler(handler)
2082
2083            return self.task_manager.shutdown()
2084        except Exception as e:
2085            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):
2088class StateType(Enum):
2089    """Enum to distinguish observation and trace states.
2090
2091    Attributes:
2092        OBSERVATION (int): Observation state.
2093        TRACE (int): Trace state.
2094    """
2095
2096    OBSERVATION = 1
2097    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:
2100class StatefulClient(object):
2101    """Base class for handling stateful operations in the Langfuse system.
2102
2103    This client is capable of creating different nested Langfuse objects like spans, generations, scores, and events,
2104    associating them with either an observation or a trace based on the specified state type.
2105
2106    Attributes:
2107        client (FernLangfuse): Core interface for Langfuse API interactions.
2108        id (str): Unique identifier of the stateful client (either observation or trace).
2109        state_type (StateType): Enum indicating whether the client is an observation or a trace.
2110        trace_id (str): Id of the trace associated with the stateful client.
2111        task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2112    """
2113
2114    log = logging.getLogger("langfuse")
2115
2116    def __init__(
2117        self,
2118        client: FernLangfuse,
2119        id: str,
2120        state_type: StateType,
2121        trace_id: str,
2122        task_manager: TaskManager,
2123    ):
2124        """Initialize the StatefulClient.
2125
2126        Args:
2127            client (FernLangfuse): Core interface for Langfuse API interactions.
2128            id (str): Unique identifier of the stateful client (either observation or trace).
2129            state_type (StateType): Enum indicating whether the client is an observation or a trace.
2130            trace_id (str): Id of the trace associated with the stateful client.
2131            task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2132        """
2133        self.client = client
2134        self.trace_id = trace_id
2135        self.id = id
2136        self.state_type = state_type
2137        self.task_manager = task_manager
2138
2139    def _add_state_to_event(self, body: dict):
2140        if self.state_type == StateType.OBSERVATION:
2141            body["parent_observation_id"] = self.id
2142            body["trace_id"] = self.trace_id
2143        else:
2144            body["trace_id"] = self.id
2145        return body
2146
2147    def _add_default_values(self, body: dict):
2148        if body.get("start_time") is None:
2149            body["start_time"] = _get_timestamp()
2150        return body
2151
2152    def generation(
2153        self,
2154        *,
2155        id: typing.Optional[str] = None,
2156        name: typing.Optional[str] = None,
2157        start_time: typing.Optional[dt.datetime] = None,
2158        end_time: typing.Optional[dt.datetime] = None,
2159        metadata: typing.Optional[typing.Any] = None,
2160        level: typing.Optional[SpanLevel] = None,
2161        status_message: typing.Optional[str] = None,
2162        version: typing.Optional[str] = None,
2163        completion_start_time: typing.Optional[dt.datetime] = None,
2164        model: typing.Optional[str] = None,
2165        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2166        input: typing.Optional[typing.Any] = None,
2167        output: typing.Optional[typing.Any] = None,
2168        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2169        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2170        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2171        prompt: typing.Optional[PromptClient] = None,
2172        **kwargs,
2173    ) -> "StatefulGenerationClient":
2174        """Create a generation nested within the current observation or trace.
2175
2176        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.
2177
2178        Args:
2179            id (Optional[str]): The id of the generation can be set, defaults to random id.
2180            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2181            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2182            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2183            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.
2184            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2185            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.
2186            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.
2187            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2188            model (Optional[str]): The name of the model used for the generation.
2189            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2190            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2191            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2192            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.
2193            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}.
2194            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}.
2195            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2196            **kwargs: Additional keyword arguments to include in the generation.
2197
2198        Returns:
2199            StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.
2200
2201        Example:
2202            ```python
2203            from langfuse import Langfuse
2204
2205            langfuse = Langfuse()
2206
2207            # Create a trace
2208            trace = langfuse.trace(name = "llm-feature")
2209
2210            # Create a nested generation in Langfuse
2211            generation = trace.generation(
2212                name="summary-generation",
2213                model="gpt-3.5-turbo",
2214                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
2215                input=[{"role": "system", "content": "You are a helpful assistant."},
2216                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
2217                metadata={"interface": "whatsapp"}
2218            )
2219            ```
2220        """
2221        generation_id = id or str(uuid.uuid4())
2222        try:
2223            generation_body = {
2224                "id": generation_id,
2225                "name": name,
2226                "start_time": start_time or _get_timestamp(),
2227                "metadata": metadata,
2228                "level": level,
2229                "status_message": status_message,
2230                "version": version,
2231                "end_time": end_time,
2232                "completion_start_time": completion_start_time,
2233                "model": model,
2234                "model_parameters": model_parameters,
2235                "input": input,
2236                "output": output,
2237                "usage": _convert_usage_input(usage) if usage is not None else None,
2238                "usage_details": usage_details,
2239                "cost_details": cost_details,
2240                **_create_prompt_context(prompt),
2241                **kwargs,
2242            }
2243
2244            generation_body = self._add_state_to_event(generation_body)
2245            new_body = self._add_default_values(generation_body)
2246
2247            new_body = CreateGenerationBody(**new_body)
2248
2249            event = {
2250                "id": str(uuid.uuid4()),
2251                "type": "generation-create",
2252                "body": new_body.dict(exclude_none=True, exclude_unset=False),
2253            }
2254
2255            self.log.debug(
2256                f"Creating generation {_filter_io_from_event_body(generation_body)}..."
2257            )
2258            self.task_manager.add_task(event)
2259
2260        except Exception as e:
2261            self.log.exception(e)
2262        finally:
2263            return StatefulGenerationClient(
2264                self.client,
2265                generation_id,
2266                StateType.OBSERVATION,
2267                self.trace_id,
2268                task_manager=self.task_manager,
2269            )
2270
2271    def span(
2272        self,
2273        *,
2274        id: typing.Optional[str] = None,
2275        name: typing.Optional[str] = None,
2276        start_time: typing.Optional[dt.datetime] = None,
2277        end_time: typing.Optional[dt.datetime] = None,
2278        metadata: typing.Optional[typing.Any] = None,
2279        input: typing.Optional[typing.Any] = None,
2280        output: typing.Optional[typing.Any] = None,
2281        level: typing.Optional[SpanLevel] = None,
2282        status_message: typing.Optional[str] = None,
2283        version: typing.Optional[str] = None,
2284        **kwargs,
2285    ) -> "StatefulSpanClient":
2286        """Create a span nested within the current observation or trace.
2287
2288        A span represents durations of units of work in a trace.
2289
2290        Args:
2291            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
2292            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2293            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2294            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2295            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2296            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.
2297            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.
2298            input (Optional[dict]): The input to the span. Can be any JSON object.
2299            output (Optional[dict]): The output to the span. Can be any JSON object.
2300            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2301            **kwargs: Additional keyword arguments to include in the span.
2302
2303        Returns:
2304            StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.
2305
2306        Example:
2307            ```python
2308            from langfuse import Langfuse
2309
2310            langfuse = Langfuse()
2311
2312            # Create a trace
2313            trace = langfuse.trace(name = "llm-feature")
2314
2315            # Create a span
2316            retrieval = langfuse.span(name = "retrieval")
2317            ```
2318        """
2319        span_id = id or str(uuid.uuid4())
2320        try:
2321            span_body = {
2322                "id": span_id,
2323                "name": name,
2324                "start_time": start_time or _get_timestamp(),
2325                "metadata": metadata,
2326                "input": input,
2327                "output": output,
2328                "level": level,
2329                "status_message": status_message,
2330                "version": version,
2331                "end_time": end_time,
2332                **kwargs,
2333            }
2334
2335            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
2336
2337            new_dict = self._add_state_to_event(span_body)
2338            new_body = self._add_default_values(new_dict)
2339
2340            event = CreateSpanBody(**new_body)
2341
2342            event = {
2343                "id": str(uuid.uuid4()),
2344                "type": "span-create",
2345                "body": event,
2346            }
2347
2348            self.task_manager.add_task(event)
2349        except Exception as e:
2350            self.log.exception(e)
2351        finally:
2352            return StatefulSpanClient(
2353                self.client,
2354                span_id,
2355                StateType.OBSERVATION,
2356                self.trace_id,
2357                task_manager=self.task_manager,
2358            )
2359
2360    @overload
2361    def score(
2362        self,
2363        *,
2364        id: typing.Optional[str] = None,
2365        name: str,
2366        value: float,
2367        data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None,
2368        comment: typing.Optional[str] = None,
2369        config_id: typing.Optional[str] = None,
2370        **kwargs,
2371    ) -> "StatefulClient": ...
2372
2373    @overload
2374    def score(
2375        self,
2376        *,
2377        id: typing.Optional[str] = None,
2378        name: str,
2379        value: str,
2380        data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL",
2381        comment: typing.Optional[str] = None,
2382        config_id: typing.Optional[str] = None,
2383        **kwargs,
2384    ) -> "StatefulClient": ...
2385
2386    def score(
2387        self,
2388        *,
2389        id: typing.Optional[str] = None,
2390        name: str,
2391        value: typing.Union[float, str],
2392        data_type: typing.Optional[ScoreDataType] = None,
2393        comment: typing.Optional[str] = None,
2394        config_id: typing.Optional[str] = None,
2395        **kwargs,
2396    ) -> "StatefulClient":
2397        """Create a score attached for the current observation or trace.
2398
2399        Args:
2400            name (str): Identifier of the score.
2401            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.
2402            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.
2403              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.
2404            comment (Optional[str]): Additional context/explanation of the score.
2405            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
2406            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
2407            **kwargs: Additional keyword arguments to include in the score.
2408
2409        Returns:
2410            StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.
2411
2412        Example:
2413            ```python
2414            from langfuse import Langfuse
2415
2416            langfuse = Langfuse()
2417
2418            # Create a trace
2419            trace = langfuse.trace(name="example-application")
2420
2421            # Add score to the trace
2422            trace = trace.score(
2423                name="user-explicit-feedback",
2424                value=0.8,
2425                comment="I like how personalized the response is"
2426            )
2427            ```
2428        """
2429        score_id = id or str(uuid.uuid4())
2430        try:
2431            new_score = {
2432                "id": score_id,
2433                "trace_id": self.trace_id,
2434                "name": name,
2435                "value": value,
2436                "data_type": data_type,
2437                "comment": comment,
2438                "config_id": config_id,
2439                **kwargs,
2440            }
2441
2442            self.log.debug(f"Creating score {new_score}...")
2443
2444            new_dict = self._add_state_to_event(new_score)
2445
2446            if self.state_type == StateType.OBSERVATION:
2447                new_dict["observationId"] = self.id
2448
2449            request = ScoreBody(**new_dict)
2450
2451            event = {
2452                "id": str(uuid.uuid4()),
2453                "type": "score-create",
2454                "body": request,
2455            }
2456
2457            self.task_manager.add_task(event)
2458
2459        except Exception as e:
2460            self.log.exception(e)
2461        finally:
2462            return StatefulClient(
2463                self.client,
2464                self.id,
2465                self.state_type,
2466                self.trace_id,
2467                task_manager=self.task_manager,
2468            )
2469
2470    def event(
2471        self,
2472        *,
2473        id: typing.Optional[str] = None,
2474        name: typing.Optional[str] = None,
2475        start_time: typing.Optional[dt.datetime] = None,
2476        metadata: typing.Optional[typing.Any] = None,
2477        input: typing.Optional[typing.Any] = None,
2478        output: typing.Optional[typing.Any] = None,
2479        level: typing.Optional[SpanLevel] = None,
2480        status_message: typing.Optional[str] = None,
2481        version: typing.Optional[str] = None,
2482        **kwargs,
2483    ) -> "StatefulClient":
2484        """Create an event nested within the current observation or trace.
2485
2486        An event represents a discrete event in a trace.
2487
2488        Args:
2489            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
2490            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
2491            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
2492            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
2493            input (Optional[Any]): The input to the event. Can be any JSON object.
2494            output (Optional[Any]): The output to the event. Can be any JSON object.
2495            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.
2496            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.
2497            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
2498            **kwargs: Additional keyword arguments to include in the event.
2499
2500        Returns:
2501            StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations.
2502
2503        Example:
2504            ```python
2505            from langfuse import Langfuse
2506
2507            langfuse = Langfuse()
2508
2509            # Create a trace
2510            trace = langfuse.trace(name = "llm-feature")
2511
2512            # Create an event
2513            retrieval = trace.event(name = "retrieval")
2514            ```
2515        """
2516        event_id = id or str(uuid.uuid4())
2517        try:
2518            event_body = {
2519                "id": event_id,
2520                "name": name,
2521                "start_time": start_time or _get_timestamp(),
2522                "metadata": metadata,
2523                "input": input,
2524                "output": output,
2525                "level": level,
2526                "status_message": status_message,
2527                "version": version,
2528                **kwargs,
2529            }
2530
2531            new_dict = self._add_state_to_event(event_body)
2532            new_body = self._add_default_values(new_dict)
2533
2534            request = CreateEventBody(**new_body)
2535
2536            event = {
2537                "id": str(uuid.uuid4()),
2538                "type": "event-create",
2539                "body": request,
2540            }
2541
2542            self.log.debug(
2543                f"Creating event {_filter_io_from_event_body(event_body)}..."
2544            )
2545            self.task_manager.add_task(event)
2546
2547        except Exception as e:
2548            self.log.exception(e)
2549        finally:
2550            return StatefulClient(
2551                self.client,
2552                event_id,
2553                StateType.OBSERVATION,
2554                self.trace_id,
2555                self.task_manager,
2556            )
2557
2558    def get_trace_url(self):
2559        """Get the URL to see the current trace in the Langfuse UI."""
2560        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.
StatefulClient( client: langfuse.api.client.FernLangfuse, id: str, state_type: StateType, trace_id: str, task_manager: langfuse._task_manager.task_manager.TaskManager)
2116    def __init__(
2117        self,
2118        client: FernLangfuse,
2119        id: str,
2120        state_type: StateType,
2121        trace_id: str,
2122        task_manager: TaskManager,
2123    ):
2124        """Initialize the StatefulClient.
2125
2126        Args:
2127            client (FernLangfuse): Core interface for Langfuse API interactions.
2128            id (str): Unique identifier of the stateful client (either observation or trace).
2129            state_type (StateType): Enum indicating whether the client is an observation or a trace.
2130            trace_id (str): Id of the trace associated with the stateful client.
2131            task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2132        """
2133        self.client = client
2134        self.trace_id = trace_id
2135        self.id = id
2136        self.state_type = state_type
2137        self.task_manager = task_manager

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
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:
2152    def generation(
2153        self,
2154        *,
2155        id: typing.Optional[str] = None,
2156        name: typing.Optional[str] = None,
2157        start_time: typing.Optional[dt.datetime] = None,
2158        end_time: typing.Optional[dt.datetime] = None,
2159        metadata: typing.Optional[typing.Any] = None,
2160        level: typing.Optional[SpanLevel] = None,
2161        status_message: typing.Optional[str] = None,
2162        version: typing.Optional[str] = None,
2163        completion_start_time: typing.Optional[dt.datetime] = None,
2164        model: typing.Optional[str] = None,
2165        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2166        input: typing.Optional[typing.Any] = None,
2167        output: typing.Optional[typing.Any] = None,
2168        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2169        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2170        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2171        prompt: typing.Optional[PromptClient] = None,
2172        **kwargs,
2173    ) -> "StatefulGenerationClient":
2174        """Create a generation nested within the current observation or trace.
2175
2176        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.
2177
2178        Args:
2179            id (Optional[str]): The id of the generation can be set, defaults to random id.
2180            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2181            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2182            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2183            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.
2184            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2185            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.
2186            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.
2187            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2188            model (Optional[str]): The name of the model used for the generation.
2189            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2190            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2191            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2192            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.
2193            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}.
2194            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}.
2195            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2196            **kwargs: Additional keyword arguments to include in the generation.
2197
2198        Returns:
2199            StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.
2200
2201        Example:
2202            ```python
2203            from langfuse import Langfuse
2204
2205            langfuse = Langfuse()
2206
2207            # Create a trace
2208            trace = langfuse.trace(name = "llm-feature")
2209
2210            # Create a nested generation in Langfuse
2211            generation = trace.generation(
2212                name="summary-generation",
2213                model="gpt-3.5-turbo",
2214                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
2215                input=[{"role": "system", "content": "You are a helpful assistant."},
2216                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
2217                metadata={"interface": "whatsapp"}
2218            )
2219            ```
2220        """
2221        generation_id = id or str(uuid.uuid4())
2222        try:
2223            generation_body = {
2224                "id": generation_id,
2225                "name": name,
2226                "start_time": start_time or _get_timestamp(),
2227                "metadata": metadata,
2228                "level": level,
2229                "status_message": status_message,
2230                "version": version,
2231                "end_time": end_time,
2232                "completion_start_time": completion_start_time,
2233                "model": model,
2234                "model_parameters": model_parameters,
2235                "input": input,
2236                "output": output,
2237                "usage": _convert_usage_input(usage) if usage is not None else None,
2238                "usage_details": usage_details,
2239                "cost_details": cost_details,
2240                **_create_prompt_context(prompt),
2241                **kwargs,
2242            }
2243
2244            generation_body = self._add_state_to_event(generation_body)
2245            new_body = self._add_default_values(generation_body)
2246
2247            new_body = CreateGenerationBody(**new_body)
2248
2249            event = {
2250                "id": str(uuid.uuid4()),
2251                "type": "generation-create",
2252                "body": new_body.dict(exclude_none=True, exclude_unset=False),
2253            }
2254
2255            self.log.debug(
2256                f"Creating generation {_filter_io_from_event_body(generation_body)}..."
2257            )
2258            self.task_manager.add_task(event)
2259
2260        except Exception as e:
2261            self.log.exception(e)
2262        finally:
2263            return StatefulGenerationClient(
2264                self.client,
2265                generation_id,
2266                StateType.OBSERVATION,
2267                self.trace_id,
2268                task_manager=self.task_manager,
2269            )

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:
2271    def span(
2272        self,
2273        *,
2274        id: typing.Optional[str] = None,
2275        name: typing.Optional[str] = None,
2276        start_time: typing.Optional[dt.datetime] = None,
2277        end_time: typing.Optional[dt.datetime] = None,
2278        metadata: typing.Optional[typing.Any] = None,
2279        input: typing.Optional[typing.Any] = None,
2280        output: typing.Optional[typing.Any] = None,
2281        level: typing.Optional[SpanLevel] = None,
2282        status_message: typing.Optional[str] = None,
2283        version: typing.Optional[str] = None,
2284        **kwargs,
2285    ) -> "StatefulSpanClient":
2286        """Create a span nested within the current observation or trace.
2287
2288        A span represents durations of units of work in a trace.
2289
2290        Args:
2291            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
2292            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2293            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2294            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2295            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2296            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.
2297            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.
2298            input (Optional[dict]): The input to the span. Can be any JSON object.
2299            output (Optional[dict]): The output to the span. Can be any JSON object.
2300            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2301            **kwargs: Additional keyword arguments to include in the span.
2302
2303        Returns:
2304            StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.
2305
2306        Example:
2307            ```python
2308            from langfuse import Langfuse
2309
2310            langfuse = Langfuse()
2311
2312            # Create a trace
2313            trace = langfuse.trace(name = "llm-feature")
2314
2315            # Create a span
2316            retrieval = langfuse.span(name = "retrieval")
2317            ```
2318        """
2319        span_id = id or str(uuid.uuid4())
2320        try:
2321            span_body = {
2322                "id": span_id,
2323                "name": name,
2324                "start_time": start_time or _get_timestamp(),
2325                "metadata": metadata,
2326                "input": input,
2327                "output": output,
2328                "level": level,
2329                "status_message": status_message,
2330                "version": version,
2331                "end_time": end_time,
2332                **kwargs,
2333            }
2334
2335            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
2336
2337            new_dict = self._add_state_to_event(span_body)
2338            new_body = self._add_default_values(new_dict)
2339
2340            event = CreateSpanBody(**new_body)
2341
2342            event = {
2343                "id": str(uuid.uuid4()),
2344                "type": "span-create",
2345                "body": event,
2346            }
2347
2348            self.task_manager.add_task(event)
2349        except Exception as e:
2350            self.log.exception(e)
2351        finally:
2352            return StatefulSpanClient(
2353                self.client,
2354                span_id,
2355                StateType.OBSERVATION,
2356                self.trace_id,
2357                task_manager=self.task_manager,
2358            )

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:
2386    def score(
2387        self,
2388        *,
2389        id: typing.Optional[str] = None,
2390        name: str,
2391        value: typing.Union[float, str],
2392        data_type: typing.Optional[ScoreDataType] = None,
2393        comment: typing.Optional[str] = None,
2394        config_id: typing.Optional[str] = None,
2395        **kwargs,
2396    ) -> "StatefulClient":
2397        """Create a score attached for the current observation or trace.
2398
2399        Args:
2400            name (str): Identifier of the score.
2401            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.
2402            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.
2403              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.
2404            comment (Optional[str]): Additional context/explanation of the score.
2405            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
2406            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
2407            **kwargs: Additional keyword arguments to include in the score.
2408
2409        Returns:
2410            StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.
2411
2412        Example:
2413            ```python
2414            from langfuse import Langfuse
2415
2416            langfuse = Langfuse()
2417
2418            # Create a trace
2419            trace = langfuse.trace(name="example-application")
2420
2421            # Add score to the trace
2422            trace = trace.score(
2423                name="user-explicit-feedback",
2424                value=0.8,
2425                comment="I like how personalized the response is"
2426            )
2427            ```
2428        """
2429        score_id = id or str(uuid.uuid4())
2430        try:
2431            new_score = {
2432                "id": score_id,
2433                "trace_id": self.trace_id,
2434                "name": name,
2435                "value": value,
2436                "data_type": data_type,
2437                "comment": comment,
2438                "config_id": config_id,
2439                **kwargs,
2440            }
2441
2442            self.log.debug(f"Creating score {new_score}...")
2443
2444            new_dict = self._add_state_to_event(new_score)
2445
2446            if self.state_type == StateType.OBSERVATION:
2447                new_dict["observationId"] = self.id
2448
2449            request = ScoreBody(**new_dict)
2450
2451            event = {
2452                "id": str(uuid.uuid4()),
2453                "type": "score-create",
2454                "body": request,
2455            }
2456
2457            self.task_manager.add_task(event)
2458
2459        except Exception as e:
2460            self.log.exception(e)
2461        finally:
2462            return StatefulClient(
2463                self.client,
2464                self.id,
2465                self.state_type,
2466                self.trace_id,
2467                task_manager=self.task_manager,
2468            )

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:
2470    def event(
2471        self,
2472        *,
2473        id: typing.Optional[str] = None,
2474        name: typing.Optional[str] = None,
2475        start_time: typing.Optional[dt.datetime] = None,
2476        metadata: typing.Optional[typing.Any] = None,
2477        input: typing.Optional[typing.Any] = None,
2478        output: typing.Optional[typing.Any] = None,
2479        level: typing.Optional[SpanLevel] = None,
2480        status_message: typing.Optional[str] = None,
2481        version: typing.Optional[str] = None,
2482        **kwargs,
2483    ) -> "StatefulClient":
2484        """Create an event nested within the current observation or trace.
2485
2486        An event represents a discrete event in a trace.
2487
2488        Args:
2489            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
2490            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
2491            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
2492            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
2493            input (Optional[Any]): The input to the event. Can be any JSON object.
2494            output (Optional[Any]): The output to the event. Can be any JSON object.
2495            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.
2496            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.
2497            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
2498            **kwargs: Additional keyword arguments to include in the event.
2499
2500        Returns:
2501            StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations.
2502
2503        Example:
2504            ```python
2505            from langfuse import Langfuse
2506
2507            langfuse = Langfuse()
2508
2509            # Create a trace
2510            trace = langfuse.trace(name = "llm-feature")
2511
2512            # Create an event
2513            retrieval = trace.event(name = "retrieval")
2514            ```
2515        """
2516        event_id = id or str(uuid.uuid4())
2517        try:
2518            event_body = {
2519                "id": event_id,
2520                "name": name,
2521                "start_time": start_time or _get_timestamp(),
2522                "metadata": metadata,
2523                "input": input,
2524                "output": output,
2525                "level": level,
2526                "status_message": status_message,
2527                "version": version,
2528                **kwargs,
2529            }
2530
2531            new_dict = self._add_state_to_event(event_body)
2532            new_body = self._add_default_values(new_dict)
2533
2534            request = CreateEventBody(**new_body)
2535
2536            event = {
2537                "id": str(uuid.uuid4()),
2538                "type": "event-create",
2539                "body": request,
2540            }
2541
2542            self.log.debug(
2543                f"Creating event {_filter_io_from_event_body(event_body)}..."
2544            )
2545            self.task_manager.add_task(event)
2546
2547        except Exception as e:
2548            self.log.exception(e)
2549        finally:
2550            return StatefulClient(
2551                self.client,
2552                event_id,
2553                StateType.OBSERVATION,
2554                self.trace_id,
2555                self.task_manager,
2556            )

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):
2558    def get_trace_url(self):
2559        """Get the URL to see the current trace in the Langfuse UI."""
2560        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):
2563class StatefulGenerationClient(StatefulClient):
2564    """Class for handling stateful operations of generations in the Langfuse system. Inherits from StatefulClient.
2565
2566    This client extends the capabilities of the StatefulClient to specifically handle generation,
2567    allowing for the creation, update, and termination of generation processes in Langfuse.
2568
2569    Attributes:
2570        client (FernLangfuse): Core interface for Langfuse API interaction.
2571        id (str): Unique identifier of the generation.
2572        state_type (StateType): Type of the stateful entity (observation or trace).
2573        trace_id (str): Id of trace associated with the generation.
2574        task_manager (TaskManager): Manager for handling asynchronous tasks.
2575    """
2576
2577    log = logging.getLogger("langfuse")
2578
2579    def __init__(
2580        self,
2581        client: FernLangfuse,
2582        id: str,
2583        state_type: StateType,
2584        trace_id: str,
2585        task_manager: TaskManager,
2586    ):
2587        """Initialize the StatefulGenerationClient."""
2588        super().__init__(client, id, state_type, trace_id, task_manager)
2589
2590    # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY
2591    def update(
2592        self,
2593        *,
2594        name: typing.Optional[str] = None,
2595        start_time: typing.Optional[dt.datetime] = None,
2596        end_time: typing.Optional[dt.datetime] = None,
2597        completion_start_time: typing.Optional[dt.datetime] = None,
2598        metadata: typing.Optional[typing.Any] = None,
2599        level: typing.Optional[SpanLevel] = None,
2600        status_message: typing.Optional[str] = None,
2601        version: typing.Optional[str] = None,
2602        model: typing.Optional[str] = None,
2603        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2604        input: typing.Optional[typing.Any] = None,
2605        output: typing.Optional[typing.Any] = None,
2606        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2607        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2608        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2609        prompt: typing.Optional[PromptClient] = None,
2610        **kwargs,
2611    ) -> "StatefulGenerationClient":
2612        """Update the generation.
2613
2614        Args:
2615            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2616            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2617            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2618            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.
2619            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2620            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.
2621            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.
2622            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2623            model (Optional[str]): The name of the model used for the generation.
2624            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2625            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2626            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2627            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.
2628            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}.
2629            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}.
2630            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2631            **kwargs: Additional keyword arguments to include in the generation.
2632
2633        Returns:
2634            StatefulGenerationClient: The updated generation. Passthrough for chaining.
2635
2636        Example:
2637            ```python
2638            from langfuse import Langfuse
2639
2640            langfuse = Langfuse()
2641
2642            # Create a trace
2643            trace = langfuse.trace(name = "llm-feature")
2644
2645            # Create a nested generation in Langfuse
2646            generation = trace.generation(name="summary-generation")
2647
2648            # Update the generation
2649            generation = generation.update(metadata={"interface": "whatsapp"})
2650            ```
2651        """
2652        try:
2653            generation_body = {
2654                "id": self.id,
2655                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2656                "name": name,
2657                "start_time": start_time,
2658                "metadata": metadata,
2659                "level": level,
2660                "status_message": status_message,
2661                "version": version,
2662                "end_time": end_time,
2663                "completion_start_time": completion_start_time,
2664                "model": model,
2665                "model_parameters": model_parameters,
2666                "input": input,
2667                "output": output,
2668                "usage": _convert_usage_input(usage) if usage is not None else None,
2669                "usage_details": usage_details,
2670                "cost_details": cost_details,
2671                **_create_prompt_context(prompt),
2672                **kwargs,
2673            }
2674
2675            self.log.debug(
2676                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2677            )
2678
2679            request = UpdateGenerationBody(**generation_body)
2680
2681            event = {
2682                "id": str(uuid.uuid4()),
2683                "type": "generation-update",
2684                "body": request.dict(exclude_none=True, exclude_unset=False),
2685            }
2686
2687            self.log.debug(
2688                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2689            )
2690            self.task_manager.add_task(event)
2691
2692        except Exception as e:
2693            self.log.exception(e)
2694        finally:
2695            return StatefulGenerationClient(
2696                self.client,
2697                self.id,
2698                StateType.OBSERVATION,
2699                self.trace_id,
2700                task_manager=self.task_manager,
2701            )
2702
2703    def end(
2704        self,
2705        *,
2706        name: typing.Optional[str] = None,
2707        start_time: typing.Optional[dt.datetime] = None,
2708        end_time: typing.Optional[dt.datetime] = None,
2709        completion_start_time: typing.Optional[dt.datetime] = None,
2710        metadata: typing.Optional[typing.Any] = None,
2711        level: typing.Optional[SpanLevel] = None,
2712        status_message: typing.Optional[str] = None,
2713        version: typing.Optional[str] = None,
2714        model: typing.Optional[str] = None,
2715        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2716        input: typing.Optional[typing.Any] = None,
2717        output: typing.Optional[typing.Any] = None,
2718        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2719        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2720        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2721        prompt: typing.Optional[PromptClient] = None,
2722        **kwargs,
2723    ) -> "StatefulGenerationClient":
2724        """End the generation, optionally updating its properties.
2725
2726        Args:
2727            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2728            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2729            end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
2730            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.
2731            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2732            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.
2733            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.
2734            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2735            model (Optional[str]): The name of the model used for the generation.
2736            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2737            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2738            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2739            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.
2740            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}.
2741            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}.
2742            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2743            **kwargs: Additional keyword arguments to include in the generation.
2744
2745        Returns:
2746            StatefulGenerationClient: The ended generation. Passthrough for chaining.
2747
2748        Example:
2749            ```python
2750            from langfuse import Langfuse
2751
2752            langfuse = Langfuse()
2753
2754            # Create a trace
2755            trace = langfuse.trace(name = "llm-feature")
2756
2757            # Create a nested generation in Langfuse
2758            generation = trace.generation(name="summary-generation")
2759
2760            # End the generation and update its properties
2761            generation = generation.end(metadata={"interface": "whatsapp"})
2762            ```
2763        """
2764        return self.update(
2765            name=name,
2766            start_time=start_time,
2767            end_time=end_time or _get_timestamp(),
2768            metadata=metadata,
2769            level=level,
2770            status_message=status_message,
2771            version=version,
2772            completion_start_time=completion_start_time,
2773            model=model,
2774            model_parameters=model_parameters,
2775            input=input,
2776            output=output,
2777            usage=usage,
2778            usage_details=usage_details,
2779            cost_details=cost_details,
2780            prompt=prompt,
2781            **kwargs,
2782        )

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)
2579    def __init__(
2580        self,
2581        client: FernLangfuse,
2582        id: str,
2583        state_type: StateType,
2584        trace_id: str,
2585        task_manager: TaskManager,
2586    ):
2587        """Initialize the StatefulGenerationClient."""
2588        super().__init__(client, id, state_type, trace_id, task_manager)

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:
2591    def update(
2592        self,
2593        *,
2594        name: typing.Optional[str] = None,
2595        start_time: typing.Optional[dt.datetime] = None,
2596        end_time: typing.Optional[dt.datetime] = None,
2597        completion_start_time: typing.Optional[dt.datetime] = None,
2598        metadata: typing.Optional[typing.Any] = None,
2599        level: typing.Optional[SpanLevel] = None,
2600        status_message: typing.Optional[str] = None,
2601        version: typing.Optional[str] = None,
2602        model: typing.Optional[str] = None,
2603        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2604        input: typing.Optional[typing.Any] = None,
2605        output: typing.Optional[typing.Any] = None,
2606        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2607        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2608        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2609        prompt: typing.Optional[PromptClient] = None,
2610        **kwargs,
2611    ) -> "StatefulGenerationClient":
2612        """Update the generation.
2613
2614        Args:
2615            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2616            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2617            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2618            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.
2619            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2620            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.
2621            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.
2622            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2623            model (Optional[str]): The name of the model used for the generation.
2624            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2625            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2626            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2627            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.
2628            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}.
2629            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}.
2630            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2631            **kwargs: Additional keyword arguments to include in the generation.
2632
2633        Returns:
2634            StatefulGenerationClient: The updated generation. Passthrough for chaining.
2635
2636        Example:
2637            ```python
2638            from langfuse import Langfuse
2639
2640            langfuse = Langfuse()
2641
2642            # Create a trace
2643            trace = langfuse.trace(name = "llm-feature")
2644
2645            # Create a nested generation in Langfuse
2646            generation = trace.generation(name="summary-generation")
2647
2648            # Update the generation
2649            generation = generation.update(metadata={"interface": "whatsapp"})
2650            ```
2651        """
2652        try:
2653            generation_body = {
2654                "id": self.id,
2655                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2656                "name": name,
2657                "start_time": start_time,
2658                "metadata": metadata,
2659                "level": level,
2660                "status_message": status_message,
2661                "version": version,
2662                "end_time": end_time,
2663                "completion_start_time": completion_start_time,
2664                "model": model,
2665                "model_parameters": model_parameters,
2666                "input": input,
2667                "output": output,
2668                "usage": _convert_usage_input(usage) if usage is not None else None,
2669                "usage_details": usage_details,
2670                "cost_details": cost_details,
2671                **_create_prompt_context(prompt),
2672                **kwargs,
2673            }
2674
2675            self.log.debug(
2676                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2677            )
2678
2679            request = UpdateGenerationBody(**generation_body)
2680
2681            event = {
2682                "id": str(uuid.uuid4()),
2683                "type": "generation-update",
2684                "body": request.dict(exclude_none=True, exclude_unset=False),
2685            }
2686
2687            self.log.debug(
2688                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2689            )
2690            self.task_manager.add_task(event)
2691
2692        except Exception as e:
2693            self.log.exception(e)
2694        finally:
2695            return StatefulGenerationClient(
2696                self.client,
2697                self.id,
2698                StateType.OBSERVATION,
2699                self.trace_id,
2700                task_manager=self.task_manager,
2701            )

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:
2703    def end(
2704        self,
2705        *,
2706        name: typing.Optional[str] = None,
2707        start_time: typing.Optional[dt.datetime] = None,
2708        end_time: typing.Optional[dt.datetime] = None,
2709        completion_start_time: typing.Optional[dt.datetime] = None,
2710        metadata: typing.Optional[typing.Any] = None,
2711        level: typing.Optional[SpanLevel] = None,
2712        status_message: typing.Optional[str] = None,
2713        version: typing.Optional[str] = None,
2714        model: typing.Optional[str] = None,
2715        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2716        input: typing.Optional[typing.Any] = None,
2717        output: typing.Optional[typing.Any] = None,
2718        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2719        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2720        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2721        prompt: typing.Optional[PromptClient] = None,
2722        **kwargs,
2723    ) -> "StatefulGenerationClient":
2724        """End the generation, optionally updating its properties.
2725
2726        Args:
2727            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2728            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2729            end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
2730            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.
2731            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2732            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.
2733            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.
2734            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2735            model (Optional[str]): The name of the model used for the generation.
2736            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2737            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2738            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2739            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.
2740            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}.
2741            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}.
2742            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2743            **kwargs: Additional keyword arguments to include in the generation.
2744
2745        Returns:
2746            StatefulGenerationClient: The ended generation. Passthrough for chaining.
2747
2748        Example:
2749            ```python
2750            from langfuse import Langfuse
2751
2752            langfuse = Langfuse()
2753
2754            # Create a trace
2755            trace = langfuse.trace(name = "llm-feature")
2756
2757            # Create a nested generation in Langfuse
2758            generation = trace.generation(name="summary-generation")
2759
2760            # End the generation and update its properties
2761            generation = generation.end(metadata={"interface": "whatsapp"})
2762            ```
2763        """
2764        return self.update(
2765            name=name,
2766            start_time=start_time,
2767            end_time=end_time or _get_timestamp(),
2768            metadata=metadata,
2769            level=level,
2770            status_message=status_message,
2771            version=version,
2772            completion_start_time=completion_start_time,
2773            model=model,
2774            model_parameters=model_parameters,
2775            input=input,
2776            output=output,
2777            usage=usage,
2778            usage_details=usage_details,
2779            cost_details=cost_details,
2780            prompt=prompt,
2781            **kwargs,
2782        )

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):
2785class StatefulSpanClient(StatefulClient):
2786    """Class for handling stateful operations of spans in the Langfuse system. Inherits from StatefulClient.
2787
2788    Attributes:
2789        client (FernLangfuse): Core interface for Langfuse API interaction.
2790        id (str): Unique identifier of the span.
2791        state_type (StateType): Type of the stateful entity (observation or trace).
2792        trace_id (str): Id of trace associated with the span.
2793        task_manager (TaskManager): Manager for handling asynchronous tasks.
2794    """
2795
2796    log = logging.getLogger("langfuse")
2797
2798    def __init__(
2799        self,
2800        client: FernLangfuse,
2801        id: str,
2802        state_type: StateType,
2803        trace_id: str,
2804        task_manager: TaskManager,
2805    ):
2806        """Initialize the StatefulSpanClient."""
2807        super().__init__(client, id, state_type, trace_id, task_manager)
2808
2809    # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY
2810    def update(
2811        self,
2812        *,
2813        name: typing.Optional[str] = None,
2814        start_time: typing.Optional[dt.datetime] = None,
2815        end_time: typing.Optional[dt.datetime] = None,
2816        metadata: typing.Optional[typing.Any] = None,
2817        input: typing.Optional[typing.Any] = None,
2818        output: typing.Optional[typing.Any] = None,
2819        level: typing.Optional[SpanLevel] = None,
2820        status_message: typing.Optional[str] = None,
2821        version: typing.Optional[str] = None,
2822        **kwargs,
2823    ) -> "StatefulSpanClient":
2824        """Update the span.
2825
2826        Args:
2827            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2828            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2829            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2830            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2831            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.
2832            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.
2833            input (Optional[dict]): The input to the span. Can be any JSON object.
2834            output (Optional[dict]): The output to the span. Can be any JSON object.
2835            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2836            **kwargs: Additional keyword arguments to include in the span.
2837
2838        Returns:
2839            StatefulSpanClient: The updated span. Passthrough for chaining.
2840
2841        Example:
2842            ```python
2843            from langfuse import Langfuse
2844
2845            langfuse = Langfuse()
2846
2847            # Create a trace
2848            trace = langfuse.trace(name = "llm-feature")
2849
2850            # Create a nested span in Langfuse
2851            span = trace.span(name="retrieval")
2852
2853            # Update the span
2854            span = span.update(metadata={"interface": "whatsapp"})
2855            ```
2856        """
2857        try:
2858            span_body = {
2859                "id": self.id,
2860                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2861                "name": name,
2862                "start_time": start_time,
2863                "metadata": metadata,
2864                "input": input,
2865                "output": output,
2866                "level": level,
2867                "status_message": status_message,
2868                "version": version,
2869                "end_time": end_time,
2870                **kwargs,
2871            }
2872            self.log.debug(f"Update span {_filter_io_from_event_body(span_body)}...")
2873
2874            request = UpdateSpanBody(**span_body)
2875
2876            event = {
2877                "id": str(uuid.uuid4()),
2878                "type": "span-update",
2879                "body": request,
2880            }
2881
2882            self.task_manager.add_task(event)
2883        except Exception as e:
2884            self.log.exception(e)
2885        finally:
2886            return StatefulSpanClient(
2887                self.client,
2888                self.id,
2889                StateType.OBSERVATION,
2890                self.trace_id,
2891                task_manager=self.task_manager,
2892            )
2893
2894    def end(
2895        self,
2896        *,
2897        name: typing.Optional[str] = None,
2898        start_time: typing.Optional[dt.datetime] = None,
2899        end_time: typing.Optional[dt.datetime] = None,
2900        metadata: typing.Optional[typing.Any] = None,
2901        input: typing.Optional[typing.Any] = None,
2902        output: typing.Optional[typing.Any] = None,
2903        level: typing.Optional[SpanLevel] = None,
2904        status_message: typing.Optional[str] = None,
2905        version: typing.Optional[str] = None,
2906        **kwargs,
2907    ) -> "StatefulSpanClient":
2908        """End the span, optionally updating its properties.
2909
2910        Args:
2911            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2912            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2913            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2914            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2915            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.
2916            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.
2917            input (Optional[dict]): The input to the span. Can be any JSON object.
2918            output (Optional[dict]): The output to the span. Can be any JSON object.
2919            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2920            **kwargs: Additional keyword arguments to include in the span.
2921
2922        Returns:
2923            StatefulSpanClient: The updated span. Passthrough for chaining.
2924
2925        Example:
2926            ```python
2927            from langfuse import Langfuse
2928
2929            langfuse = Langfuse()
2930
2931            # Create a trace
2932            trace = langfuse.trace(name = "llm-feature")
2933
2934            # Create a nested span in Langfuse
2935            span = trace.span(name="retrieval")
2936
2937            # End the span and update its properties
2938            span = span.end(metadata={"interface": "whatsapp"})
2939            ```
2940        """
2941        try:
2942            span_body = {
2943                "name": name,
2944                "start_time": start_time,
2945                "metadata": metadata,
2946                "input": input,
2947                "output": output,
2948                "level": level,
2949                "status_message": status_message,
2950                "version": version,
2951                "end_time": end_time or _get_timestamp(),
2952                **kwargs,
2953            }
2954            return self.update(**span_body)
2955
2956        except Exception as e:
2957            self.log.warning(e)
2958        finally:
2959            return StatefulSpanClient(
2960                self.client,
2961                self.id,
2962                StateType.OBSERVATION,
2963                self.trace_id,
2964                task_manager=self.task_manager,
2965            )
2966
2967    def get_langchain_handler(self, update_parent: bool = False):
2968        """Get langchain callback handler associated with the current span.
2969
2970        Args:
2971            update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
2972
2973        Returns:
2974            CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
2975        """
2976        from langfuse.callback import CallbackHandler
2977
2978        return CallbackHandler(
2979            stateful_client=self, update_stateful_client=update_parent
2980        )

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)
2798    def __init__(
2799        self,
2800        client: FernLangfuse,
2801        id: str,
2802        state_type: StateType,
2803        trace_id: str,
2804        task_manager: TaskManager,
2805    ):
2806        """Initialize the StatefulSpanClient."""
2807        super().__init__(client, id, state_type, trace_id, task_manager)

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:
2810    def update(
2811        self,
2812        *,
2813        name: typing.Optional[str] = None,
2814        start_time: typing.Optional[dt.datetime] = None,
2815        end_time: typing.Optional[dt.datetime] = None,
2816        metadata: typing.Optional[typing.Any] = None,
2817        input: typing.Optional[typing.Any] = None,
2818        output: typing.Optional[typing.Any] = None,
2819        level: typing.Optional[SpanLevel] = None,
2820        status_message: typing.Optional[str] = None,
2821        version: typing.Optional[str] = None,
2822        **kwargs,
2823    ) -> "StatefulSpanClient":
2824        """Update the span.
2825
2826        Args:
2827            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2828            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2829            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2830            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2831            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.
2832            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.
2833            input (Optional[dict]): The input to the span. Can be any JSON object.
2834            output (Optional[dict]): The output to the span. Can be any JSON object.
2835            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2836            **kwargs: Additional keyword arguments to include in the span.
2837
2838        Returns:
2839            StatefulSpanClient: The updated span. Passthrough for chaining.
2840
2841        Example:
2842            ```python
2843            from langfuse import Langfuse
2844
2845            langfuse = Langfuse()
2846
2847            # Create a trace
2848            trace = langfuse.trace(name = "llm-feature")
2849
2850            # Create a nested span in Langfuse
2851            span = trace.span(name="retrieval")
2852
2853            # Update the span
2854            span = span.update(metadata={"interface": "whatsapp"})
2855            ```
2856        """
2857        try:
2858            span_body = {
2859                "id": self.id,
2860                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2861                "name": name,
2862                "start_time": start_time,
2863                "metadata": metadata,
2864                "input": input,
2865                "output": output,
2866                "level": level,
2867                "status_message": status_message,
2868                "version": version,
2869                "end_time": end_time,
2870                **kwargs,
2871            }
2872            self.log.debug(f"Update span {_filter_io_from_event_body(span_body)}...")
2873
2874            request = UpdateSpanBody(**span_body)
2875
2876            event = {
2877                "id": str(uuid.uuid4()),
2878                "type": "span-update",
2879                "body": request,
2880            }
2881
2882            self.task_manager.add_task(event)
2883        except Exception as e:
2884            self.log.exception(e)
2885        finally:
2886            return StatefulSpanClient(
2887                self.client,
2888                self.id,
2889                StateType.OBSERVATION,
2890                self.trace_id,
2891                task_manager=self.task_manager,
2892            )

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:
2894    def end(
2895        self,
2896        *,
2897        name: typing.Optional[str] = None,
2898        start_time: typing.Optional[dt.datetime] = None,
2899        end_time: typing.Optional[dt.datetime] = None,
2900        metadata: typing.Optional[typing.Any] = None,
2901        input: typing.Optional[typing.Any] = None,
2902        output: typing.Optional[typing.Any] = None,
2903        level: typing.Optional[SpanLevel] = None,
2904        status_message: typing.Optional[str] = None,
2905        version: typing.Optional[str] = None,
2906        **kwargs,
2907    ) -> "StatefulSpanClient":
2908        """End the span, optionally updating its properties.
2909
2910        Args:
2911            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2912            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2913            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2914            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2915            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.
2916            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.
2917            input (Optional[dict]): The input to the span. Can be any JSON object.
2918            output (Optional[dict]): The output to the span. Can be any JSON object.
2919            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2920            **kwargs: Additional keyword arguments to include in the span.
2921
2922        Returns:
2923            StatefulSpanClient: The updated span. Passthrough for chaining.
2924
2925        Example:
2926            ```python
2927            from langfuse import Langfuse
2928
2929            langfuse = Langfuse()
2930
2931            # Create a trace
2932            trace = langfuse.trace(name = "llm-feature")
2933
2934            # Create a nested span in Langfuse
2935            span = trace.span(name="retrieval")
2936
2937            # End the span and update its properties
2938            span = span.end(metadata={"interface": "whatsapp"})
2939            ```
2940        """
2941        try:
2942            span_body = {
2943                "name": name,
2944                "start_time": start_time,
2945                "metadata": metadata,
2946                "input": input,
2947                "output": output,
2948                "level": level,
2949                "status_message": status_message,
2950                "version": version,
2951                "end_time": end_time or _get_timestamp(),
2952                **kwargs,
2953            }
2954            return self.update(**span_body)
2955
2956        except Exception as e:
2957            self.log.warning(e)
2958        finally:
2959            return StatefulSpanClient(
2960                self.client,
2961                self.id,
2962                StateType.OBSERVATION,
2963                self.trace_id,
2964                task_manager=self.task_manager,
2965            )

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):
2967    def get_langchain_handler(self, update_parent: bool = False):
2968        """Get langchain callback handler associated with the current span.
2969
2970        Args:
2971            update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
2972
2973        Returns:
2974            CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
2975        """
2976        from langfuse.callback import CallbackHandler
2977
2978        return CallbackHandler(
2979            stateful_client=self, update_stateful_client=update_parent
2980        )

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):
2983class StatefulTraceClient(StatefulClient):
2984    """Class for handling stateful operations of traces in the Langfuse system. Inherits from StatefulClient.
2985
2986    Attributes:
2987        client (FernLangfuse): Core interface for Langfuse API interaction.
2988        id (str): Unique identifier of the trace.
2989        state_type (StateType): Type of the stateful entity (observation or trace).
2990        trace_id (str): The trace ID associated with this client.
2991        task_manager (TaskManager): Manager for handling asynchronous tasks.
2992    """
2993
2994    log = logging.getLogger("langfuse")
2995
2996    def __init__(
2997        self,
2998        client: FernLangfuse,
2999        id: str,
3000        state_type: StateType,
3001        trace_id: str,
3002        task_manager: TaskManager,
3003    ):
3004        """Initialize the StatefulTraceClient."""
3005        super().__init__(client, id, state_type, trace_id, task_manager)
3006        self.task_manager = task_manager
3007
3008    def update(
3009        self,
3010        *,
3011        name: typing.Optional[str] = None,
3012        user_id: typing.Optional[str] = None,
3013        session_id: typing.Optional[str] = None,
3014        version: typing.Optional[str] = None,
3015        release: typing.Optional[str] = None,
3016        input: typing.Optional[typing.Any] = None,
3017        output: typing.Optional[typing.Any] = None,
3018        metadata: typing.Optional[typing.Any] = None,
3019        tags: typing.Optional[typing.List[str]] = None,
3020        public: typing.Optional[bool] = None,
3021        **kwargs,
3022    ) -> "StatefulTraceClient":
3023        """Update the trace.
3024
3025        Args:
3026            name: Identifier of the trace. Useful for sorting/filtering in the UI.
3027            input: The input of the trace. Can be any JSON object.
3028            output: The output of the trace. Can be any JSON object.
3029            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
3030            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
3031            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
3032            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
3033            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
3034            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.
3035            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.
3036            **kwargs: Additional keyword arguments that can be included in the trace.
3037
3038        Returns:
3039            StatefulTraceClient: The updated trace. Passthrough for chaining.
3040
3041        Example:
3042            ```python
3043            from langfuse import Langfuse
3044
3045            langfuse = Langfuse()
3046
3047            # Create a trace
3048            trace = langfuse.trace(
3049                name="example-application",
3050                user_id="user-1234")
3051            )
3052
3053            # Update the trace
3054            trace = trace.update(
3055                output={"result": "success"},
3056                metadata={"interface": "whatsapp"}
3057            )
3058            ```
3059        """
3060        try:
3061            trace_body = {
3062                "id": self.id,
3063                "name": name,
3064                "userId": user_id,
3065                "sessionId": session_id
3066                or kwargs.get("sessionId", None),  # backward compatibility
3067                "version": version,
3068                "release": release,
3069                "input": input,
3070                "output": output,
3071                "metadata": metadata,
3072                "public": public,
3073                "tags": tags,
3074                **kwargs,
3075            }
3076            self.log.debug(f"Update trace {_filter_io_from_event_body(trace_body)}...")
3077
3078            request = TraceBody(**trace_body)
3079
3080            event = {
3081                "id": str(uuid.uuid4()),
3082                "type": "trace-create",
3083                "body": request,
3084            }
3085
3086            self.task_manager.add_task(event)
3087
3088        except Exception as e:
3089            self.log.exception(e)
3090        finally:
3091            return StatefulTraceClient(
3092                self.client,
3093                self.id,
3094                StateType.TRACE,
3095                self.trace_id,
3096                task_manager=self.task_manager,
3097            )
3098
3099    def get_langchain_handler(self, update_parent: bool = False):
3100        """Get langchain callback handler associated with the current trace.
3101
3102        This method creates and returns a CallbackHandler instance, linking it with the current
3103        trace. Use this if you want to group multiple Langchain runs within a single trace.
3104
3105        Args:
3106            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
3107
3108        Raises:
3109            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
3110
3111        Returns:
3112            CallbackHandler: Langchain callback handler linked to the current trace.
3113
3114        Example:
3115            ```python
3116            from langfuse import Langfuse
3117
3118            langfuse = Langfuse()
3119
3120            # Create a trace
3121            trace = langfuse.trace(name = "llm-feature")
3122
3123            # Get a langchain callback handler
3124            handler = trace.get_langchain_handler()
3125            ```
3126        """
3127        try:
3128            from langfuse.callback import CallbackHandler
3129
3130            self.log.debug(f"Creating new handler for trace {self.id}")
3131
3132            return CallbackHandler(
3133                stateful_client=self,
3134                debug=self.log.level == logging.DEBUG,
3135                update_stateful_client=update_parent,
3136            )
3137        except Exception as e:
3138            self.log.exception(e)
3139
3140    def getNewHandler(self):
3141        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
3142        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)
2996    def __init__(
2997        self,
2998        client: FernLangfuse,
2999        id: str,
3000        state_type: StateType,
3001        trace_id: str,
3002        task_manager: TaskManager,
3003    ):
3004        """Initialize the StatefulTraceClient."""
3005        super().__init__(client, id, state_type, trace_id, task_manager)
3006        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:
3008    def update(
3009        self,
3010        *,
3011        name: typing.Optional[str] = None,
3012        user_id: typing.Optional[str] = None,
3013        session_id: typing.Optional[str] = None,
3014        version: typing.Optional[str] = None,
3015        release: typing.Optional[str] = None,
3016        input: typing.Optional[typing.Any] = None,
3017        output: typing.Optional[typing.Any] = None,
3018        metadata: typing.Optional[typing.Any] = None,
3019        tags: typing.Optional[typing.List[str]] = None,
3020        public: typing.Optional[bool] = None,
3021        **kwargs,
3022    ) -> "StatefulTraceClient":
3023        """Update the trace.
3024
3025        Args:
3026            name: Identifier of the trace. Useful for sorting/filtering in the UI.
3027            input: The input of the trace. Can be any JSON object.
3028            output: The output of the trace. Can be any JSON object.
3029            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
3030            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
3031            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
3032            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
3033            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
3034            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.
3035            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.
3036            **kwargs: Additional keyword arguments that can be included in the trace.
3037
3038        Returns:
3039            StatefulTraceClient: The updated trace. Passthrough for chaining.
3040
3041        Example:
3042            ```python
3043            from langfuse import Langfuse
3044
3045            langfuse = Langfuse()
3046
3047            # Create a trace
3048            trace = langfuse.trace(
3049                name="example-application",
3050                user_id="user-1234")
3051            )
3052
3053            # Update the trace
3054            trace = trace.update(
3055                output={"result": "success"},
3056                metadata={"interface": "whatsapp"}
3057            )
3058            ```
3059        """
3060        try:
3061            trace_body = {
3062                "id": self.id,
3063                "name": name,
3064                "userId": user_id,
3065                "sessionId": session_id
3066                or kwargs.get("sessionId", None),  # backward compatibility
3067                "version": version,
3068                "release": release,
3069                "input": input,
3070                "output": output,
3071                "metadata": metadata,
3072                "public": public,
3073                "tags": tags,
3074                **kwargs,
3075            }
3076            self.log.debug(f"Update trace {_filter_io_from_event_body(trace_body)}...")
3077
3078            request = TraceBody(**trace_body)
3079
3080            event = {
3081                "id": str(uuid.uuid4()),
3082                "type": "trace-create",
3083                "body": request,
3084            }
3085
3086            self.task_manager.add_task(event)
3087
3088        except Exception as e:
3089            self.log.exception(e)
3090        finally:
3091            return StatefulTraceClient(
3092                self.client,
3093                self.id,
3094                StateType.TRACE,
3095                self.trace_id,
3096                task_manager=self.task_manager,
3097            )

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):
3099    def get_langchain_handler(self, update_parent: bool = False):
3100        """Get langchain callback handler associated with the current trace.
3101
3102        This method creates and returns a CallbackHandler instance, linking it with the current
3103        trace. Use this if you want to group multiple Langchain runs within a single trace.
3104
3105        Args:
3106            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
3107
3108        Raises:
3109            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
3110
3111        Returns:
3112            CallbackHandler: Langchain callback handler linked to the current trace.
3113
3114        Example:
3115            ```python
3116            from langfuse import Langfuse
3117
3118            langfuse = Langfuse()
3119
3120            # Create a trace
3121            trace = langfuse.trace(name = "llm-feature")
3122
3123            # Get a langchain callback handler
3124            handler = trace.get_langchain_handler()
3125            ```
3126        """
3127        try:
3128            from langfuse.callback import CallbackHandler
3129
3130            self.log.debug(f"Creating new handler for trace {self.id}")
3131
3132            return CallbackHandler(
3133                stateful_client=self,
3134                debug=self.log.level == logging.DEBUG,
3135                update_stateful_client=update_parent,
3136            )
3137        except Exception as e:
3138            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):
3140    def getNewHandler(self):
3141        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
3142        return self.get_langchain_handler()

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

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

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]] = {}):
3354    @contextmanager
3355    def observe_llama_index(
3356        self,
3357        *,
3358        run_name: str,
3359        run_description: Optional[str] = None,
3360        run_metadata: Optional[Any] = None,
3361        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3362    ):
3363        """Context manager for observing LlamaIndex operations linked to this dataset item.
3364
3365        This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging
3366        and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all
3367        operations performed within the context are linked to the appropriate dataset item and run in Langfuse.
3368
3369        Args:
3370            run_name (str): The name of the dataset run.
3371            run_description (Optional[str]): Description of the dataset run. Defaults to None.
3372            run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
3373            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass
3374                to the LlamaIndex integration constructor. Defaults to an empty dictionary.
3375
3376        Yields:
3377            LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.
3378
3379        Example:
3380            ```python
3381            dataset_item = dataset.items[0]
3382
3383            with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler:
3384                # Perform LlamaIndex operations here
3385                some_llama_index_operation()
3386            ```
3387
3388        Raises:
3389            ImportError: If required modules for LlamaIndex integration are not available.
3390        """
3391        metadata = {
3392            "dataset_item_id": self.id,
3393            "run_name": run_name,
3394            "dataset_id": self.dataset_id,
3395        }
3396        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3397        self.link(
3398            trace, run_name, run_metadata=run_metadata, run_description=run_description
3399        )
3400
3401        try:
3402            import llama_index.core
3403            from llama_index.core import Settings
3404            from llama_index.core.callbacks import CallbackManager
3405
3406            from langfuse.llama_index import LlamaIndexCallbackHandler
3407
3408            callback_handler = LlamaIndexCallbackHandler(
3409                **llama_index_integration_constructor_kwargs,
3410            )
3411            callback_handler.set_root(trace, update_root=True)
3412
3413            # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler
3414            # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it
3415            prev_global_handler = llama_index.core.global_handler
3416            prev_langfuse_handler = None
3417
3418            if isinstance(prev_global_handler, LlamaIndexCallbackHandler):
3419                llama_index.core.global_handler = None
3420
3421            if Settings.callback_manager is None:
3422                Settings.callback_manager = CallbackManager([callback_handler])
3423            else:
3424                for handler in Settings.callback_manager.handlers:
3425                    if isinstance(handler, LlamaIndexCallbackHandler):
3426                        prev_langfuse_handler = handler
3427                        Settings.callback_manager.remove_handler(handler)
3428
3429                Settings.callback_manager.add_handler(callback_handler)
3430
3431        except Exception as e:
3432            self.log.exception(e)
3433
3434        try:
3435            yield callback_handler
3436        finally:
3437            # Reset the handlers
3438            Settings.callback_manager.remove_handler(callback_handler)
3439            if prev_langfuse_handler is not None:
3440                Settings.callback_manager.add_handler(prev_langfuse_handler)
3441
3442            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]] = {}):
3444    def get_llama_index_handler(
3445        self,
3446        *,
3447        run_name: str,
3448        run_description: Optional[str] = None,
3449        run_metadata: Optional[Any] = None,
3450        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3451    ):
3452        """Create and get a llama-index callback handler linked to this dataset item.
3453
3454        Args:
3455            run_name (str): The name of the dataset run to be used in the callback handler.
3456            run_description (Optional[str]): Description of the dataset run.
3457            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3458            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
3459
3460        Returns:
3461            LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.
3462        """
3463        metadata = {
3464            "dataset_item_id": self.id,
3465            "run_name": run_name,
3466            "dataset_id": self.dataset_id,
3467        }
3468        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3469
3470        self.link(
3471            trace, run_name, run_metadata=run_metadata, run_description=run_description
3472        )
3473
3474        try:
3475            from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler
3476
3477            callback_handler = LlamaIndexCallbackHandler(
3478                **llama_index_integration_constructor_kwargs,
3479            )
3480            callback_handler.set_root(trace, update_root=True)
3481
3482            return callback_handler
3483        except Exception as e:
3484            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:
3487class DatasetClient:
3488    """Class for managing datasets in Langfuse.
3489
3490    Attributes:
3491        id (str): Unique identifier of the dataset.
3492        name (str): Name of the dataset.
3493        description (Optional[str]): Description of the dataset.
3494        metadata (Optional[typing.Any]): Additional metadata of the dataset.
3495        project_id (str): Identifier of the project to which the dataset belongs.
3496        dataset_name (str): Name of the dataset.
3497        created_at (datetime): Timestamp of dataset creation.
3498        updated_at (datetime): Timestamp of the last update to the dataset.
3499        items (List[DatasetItemClient]): List of dataset items associated with the dataset.
3500        runs (List[str]): List of dataset runs associated with the dataset. Deprecated.
3501
3502    Example:
3503        Print the input of each dataset item in a dataset.
3504        ```python
3505        from langfuse import Langfuse
3506
3507        langfuse = Langfuse()
3508
3509        dataset = langfuse.get_dataset("<dataset_name>")
3510
3511        for item in dataset.items:
3512            print(item.input)
3513        ```
3514    """
3515
3516    id: str
3517    name: str
3518    description: Optional[str]
3519    project_id: str
3520    dataset_name: str  # for backward compatibility, to be deprecated
3521    metadata: Optional[Any]
3522    created_at: dt.datetime
3523    updated_at: dt.datetime
3524    items: typing.List[DatasetItemClient]
3525    runs: typing.List[str] = []  # deprecated
3526
3527    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3528        """Initialize the DatasetClient."""
3529        self.id = dataset.id
3530        self.name = dataset.name
3531        self.description = dataset.description
3532        self.project_id = dataset.project_id
3533        self.metadata = dataset.metadata
3534        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3535        self.created_at = dataset.created_at
3536        self.updated_at = dataset.updated_at
3537        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])
3527    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3528        """Initialize the DatasetClient."""
3529        self.id = dataset.id
3530        self.name = dataset.name
3531        self.description = dataset.description
3532        self.project_id = dataset.project_id
3533        self.metadata = dataset.metadata
3534        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3535        self.created_at = dataset.created_at
3536        self.updated_at = dataset.updated_at
3537        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] = []