langfuse.client

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

Response object for fetch_traces method.

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

Response object for fetch_trace method.

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

Response object for fetch_observations method.

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

Response object for fetch_observation method.

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

Response object for fetch_media method.

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

Response object for fetch_sessions method.

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

Langfuse Python client.

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

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

import os
from langfuse import Langfuse

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

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

Initialize the Langfuse client.

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

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

import os
from langfuse import Langfuse

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

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

Logger for the Langfuse client.

host: str

Host of Langfuse API.

project_id: Optional[str]

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

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

Get the current trace id.

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

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

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

Fetch a dataset by its name.

Arguments:
  • name (str): The name of the dataset to fetch.
  • fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
Returns:

DatasetClient: The dataset with the given name.

def get_dataset_item(self, id: str) -> DatasetItemClient:
432    def get_dataset_item(self, id: str) -> "DatasetItemClient":
433        """Get the dataset item with the given id."""
434        try:
435            self.log.debug(f"Getting dataset item {id}")
436            dataset_item = self.client.dataset_items.get(id=id)
437            return DatasetItemClient(dataset_item, langfuse=self)
438        except Exception as e:
439            handle_fern_exception(e)
440            raise e

Get the dataset item with the given id.

def auth_check(self) -> bool:
442    def auth_check(self) -> bool:
443        """Check if the provided credentials (public and secret key) are valid.
444
445        Raises:
446            Exception: If no projects were found for the provided credentials.
447
448        Note:
449            This method is blocking. It is discouraged to use it in production code.
450        """
451        try:
452            projects = self.client.projects.get()
453            self.log.debug(
454                f"Auth check successful, found {len(projects.data)} projects"
455            )
456            if len(projects.data) == 0:
457                raise Exception(
458                    "Auth check failed, no project found for the keys provided."
459                )
460            return True
461
462        except Exception as e:
463            handle_fern_exception(e)
464            raise e

Check if the provided credentials (public and secret key) are valid.

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

This method is blocking. It is discouraged to use it in production code.

def get_dataset_runs( self, dataset_name: str, *, page: Optional[int] = None, limit: Optional[int] = None) -> langfuse.api.PaginatedDatasetRuns:
466    def get_dataset_runs(
467        self,
468        dataset_name: str,
469        *,
470        page: typing.Optional[int] = None,
471        limit: typing.Optional[int] = None,
472    ) -> PaginatedDatasetRuns:
473        """Get all dataset runs.
474
475        Args:
476            dataset_name (str): Name of the dataset.
477            page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None.
478            limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50.
479
480        Returns:
481            PaginatedDatasetRuns: The dataset runs.
482        """
483        try:
484            self.log.debug("Getting dataset runs")
485            return self.client.datasets.get_runs(
486                dataset_name=self._url_encode(dataset_name), page=page, limit=limit
487            )
488        except Exception as e:
489            handle_fern_exception(e)
490            raise e

Get all dataset runs.

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

PaginatedDatasetRuns: The dataset runs.

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

Get a dataset run.

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

DatasetRunWithItems: The dataset run.

def create_dataset( self, name: str, description: Optional[str] = None, metadata: Optional[Any] = None) -> langfuse.api.Dataset:
518    def create_dataset(
519        self,
520        name: str,
521        description: Optional[str] = None,
522        metadata: Optional[Any] = None,
523    ) -> Dataset:
524        """Create a dataset with the given name on Langfuse.
525
526        Args:
527            name: Name of the dataset to create.
528            description: Description of the dataset. Defaults to None.
529            metadata: Additional metadata. Defaults to None.
530
531        Returns:
532            Dataset: The created dataset as returned by the Langfuse API.
533        """
534        try:
535            body = CreateDatasetRequest(
536                name=name, description=description, metadata=metadata
537            )
538            self.log.debug(f"Creating datasets {body}")
539            return self.client.datasets.create(request=body)
540        except Exception as e:
541            handle_fern_exception(e)
542            raise e

Create a dataset with the given name on Langfuse.

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

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

def create_dataset_item( self, dataset_name: str, input: Optional[Any] = None, expected_output: Optional[Any] = None, metadata: Optional[Any] = None, source_trace_id: Optional[str] = None, source_observation_id: Optional[str] = None, status: Optional[langfuse.api.DatasetStatus] = None, id: Optional[str] = None) -> langfuse.api.DatasetItem:
544    def create_dataset_item(
545        self,
546        dataset_name: str,
547        input: Optional[Any] = None,
548        expected_output: Optional[Any] = None,
549        metadata: Optional[Any] = None,
550        source_trace_id: Optional[str] = None,
551        source_observation_id: Optional[str] = None,
552        status: Optional[DatasetStatus] = None,
553        id: Optional[str] = None,
554    ) -> DatasetItem:
555        """Create a dataset item.
556
557        Upserts if an item with id already exists.
558
559        Args:
560            dataset_name: Name of the dataset in which the dataset item should be created.
561            input: Input data. Defaults to None. Can contain any dict, list or scalar.
562            expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
563            metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
564            source_trace_id: Id of the source trace. Defaults to None.
565            source_observation_id: Id of the source observation. Defaults to None.
566            status: Status of the dataset item. Defaults to ACTIVE for newly created items.
567            id: Id of the dataset item. Defaults to None. Provide your own id if you want to dedupe dataset items. Id needs to be globally unique and cannot be reused across datasets.
568
569        Returns:
570            DatasetItem: The created dataset item as returned by the Langfuse API.
571
572        Example:
573            ```python
574            from langfuse import Langfuse
575
576            langfuse = Langfuse()
577
578            # Uploading items to the Langfuse dataset named "capital_cities"
579            langfuse.create_dataset_item(
580                dataset_name="capital_cities",
581                input={"input": {"country": "Italy"}},
582                expected_output={"expected_output": "Rome"},
583                metadata={"foo": "bar"}
584            )
585            ```
586        """
587        try:
588            body = CreateDatasetItemRequest(
589                datasetName=dataset_name,
590                input=input,
591                expectedOutput=expected_output,
592                metadata=metadata,
593                sourceTraceId=source_trace_id,
594                sourceObservationId=source_observation_id,
595                status=status,
596                id=id,
597            )
598            self.log.debug(f"Creating dataset item {body}")
599            return self.client.dataset_items.create(request=body)
600        except Exception as e:
601            handle_fern_exception(e)
602            raise e

Create a dataset item.

Upserts if an item with id already exists.

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

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

Example:
from langfuse import Langfuse

langfuse = Langfuse()

# Uploading items to the Langfuse dataset named "capital_cities"
langfuse.create_dataset_item(
    dataset_name="capital_cities",
    input={"input": {"country": "Italy"}},
    expected_output={"expected_output": "Rome"},
    metadata={"foo": "bar"}
)
def fetch_trace(self, id: str) -> FetchTraceResponse:
604    def fetch_trace(
605        self,
606        id: str,
607    ) -> FetchTraceResponse:
608        """Fetch a trace via the Langfuse API by its id.
609
610        Args:
611            id: The id of the trace to fetch.
612
613        Returns:
614            FetchTraceResponse: The trace with full details as returned by the Langfuse API on `data`.
615
616        Raises:
617            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
618        """
619        try:
620            self.log.debug(f"Getting trace {id}")
621            trace = self.client.trace.get(id)
622            return FetchTraceResponse(data=trace)
623        except Exception as e:
624            handle_fern_exception(e)
625            raise e

Fetch a trace via the Langfuse API by its id.

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

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

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

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

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

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

Raises:
  • Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
def fetch_traces( self, *, page: Optional[int] = None, limit: Optional[int] = None, user_id: Optional[str] = None, name: Optional[str] = None, session_id: Optional[str] = None, from_timestamp: Optional[datetime.datetime] = None, to_timestamp: Optional[datetime.datetime] = None, environment: Union[str, Sequence[str], NoneType] = None, order_by: Optional[str] = None, tags: Union[str, Sequence[str], NoneType] = None) -> FetchTracesResponse:
654    def fetch_traces(
655        self,
656        *,
657        page: Optional[int] = None,
658        limit: Optional[int] = None,
659        user_id: Optional[str] = None,
660        name: Optional[str] = None,
661        session_id: Optional[str] = None,
662        from_timestamp: Optional[dt.datetime] = None,
663        to_timestamp: Optional[dt.datetime] = None,
664        environment: Optional[Union[str, Sequence[str]]] = None,
665        order_by: Optional[str] = None,
666        tags: Optional[Union[str, Sequence[str]]] = None,
667    ) -> FetchTracesResponse:
668        """Fetch a list of traces in the current project matching the given parameters.
669
670        Args:
671            page (Optional[int]): Page number, starts at 1. Defaults to None.
672            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.
673            name (Optional[str]): Filter by name of traces. Defaults to None.
674            user_id (Optional[str]): Filter by user_id. Defaults to None.
675            session_id (Optional[str]): Filter by session_id. Defaults to None.
676            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
677            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
678            environment (Optional[Union[str, Sequence[str]]]): Filter by environment. Defaults to None.
679            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.
680            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
681
682        Returns:
683            FetchTracesResponse, list of traces on `data` and metadata on `meta`.
684
685        Raises:
686            Exception: If an error occurred during the request.
687        """
688        try:
689            self.log.debug(
690                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {environment}, {order_by}, {tags}"
691            )
692            res = self.client.trace.list(
693                page=page,
694                limit=limit,
695                name=name,
696                user_id=user_id,
697                session_id=session_id,
698                from_timestamp=from_timestamp,
699                to_timestamp=to_timestamp,
700                environment=environment,
701                order_by=order_by,
702                tags=tags,
703            )
704            return FetchTracesResponse(data=res.data, meta=res.meta)
705        except Exception as e:
706            handle_fern_exception(e)
707            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.
  • environment (Optional[Union[str, Sequence[str]]]): Filter by environment. 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:
709    def get_traces(
710        self,
711        *,
712        page: Optional[int] = None,
713        limit: Optional[int] = None,
714        user_id: Optional[str] = None,
715        name: Optional[str] = None,
716        session_id: Optional[str] = None,
717        from_timestamp: Optional[dt.datetime] = None,
718        to_timestamp: Optional[dt.datetime] = None,
719        order_by: Optional[str] = None,
720        tags: Optional[Union[str, Sequence[str]]] = None,
721    ) -> Traces:
722        """Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead.
723
724        Args:
725            page (Optional[int]): Page number, starts at 1. Defaults to None.
726            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.
727            name (Optional[str]): Filter by name of traces. Defaults to None.
728            user_id (Optional[str]): Filter by user_id. Defaults to None.
729            session_id (Optional[str]): Filter by session_id. Defaults to None.
730            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
731            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
732            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.
733            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
734
735        Returns:
736            List of Traces
737
738        Raises:
739            Exception: If an error occurred during the request.
740        """
741        warnings.warn(
742            "get_traces is deprecated, use fetch_traces instead.",
743            DeprecationWarning,
744        )
745        try:
746            self.log.debug(
747                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
748            )
749            return self.client.trace.list(
750                page=page,
751                limit=limit,
752                name=name,
753                user_id=user_id,
754                session_id=session_id,
755                from_timestamp=from_timestamp,
756                to_timestamp=to_timestamp,
757                order_by=order_by,
758                tags=tags,
759            )
760        except Exception as e:
761            handle_fern_exception(e)
762            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, environment: Union[str, Sequence[str], NoneType] = None, type: Optional[str] = None) -> FetchObservationsResponse:
764    def fetch_observations(
765        self,
766        *,
767        page: typing.Optional[int] = None,
768        limit: typing.Optional[int] = None,
769        name: typing.Optional[str] = None,
770        user_id: typing.Optional[str] = None,
771        trace_id: typing.Optional[str] = None,
772        parent_observation_id: typing.Optional[str] = None,
773        from_start_time: typing.Optional[dt.datetime] = None,
774        to_start_time: typing.Optional[dt.datetime] = None,
775        environment: Optional[Union[str, Sequence[str]]] = None,
776        type: typing.Optional[str] = None,
777    ) -> FetchObservationsResponse:
778        """Get a list of observations in the current project matching the given parameters.
779
780        Args:
781            page (Optional[int]): Page number of the observations to return. Defaults to None.
782            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
783            name (Optional[str]): Name of the observations to return. Defaults to None.
784            user_id (Optional[str]): User identifier. Defaults to None.
785            trace_id (Optional[str]): Trace identifier. Defaults to None.
786            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
787            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
788            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
789            environment (Optional[Union[str, Sequence[str]]]): Filter by environment. Defaults to None.
790            type (Optional[str]): Type of the observation. Defaults to None.
791
792        Returns:
793            FetchObservationsResponse, list of observations on `data` and metadata on `meta`.
794
795        Raises:
796            Exception: If an error occurred during the request.
797        """
798        try:
799            self.log.debug(
800                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {environment}, {type}"
801            )
802            res = self.client.observations.get_many(
803                page=page,
804                limit=limit,
805                name=name,
806                user_id=user_id,
807                trace_id=trace_id,
808                parent_observation_id=parent_observation_id,
809                from_start_time=from_start_time,
810                to_start_time=to_start_time,
811                environment=environment,
812                type=type,
813            )
814            return FetchObservationsResponse(data=res.data, meta=res.meta)
815        except Exception as e:
816            self.log.exception(e)
817            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.
  • environment (Optional[Union[str, Sequence[str]]]): Filter by environment. 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:
819    def get_observations(
820        self,
821        *,
822        page: typing.Optional[int] = None,
823        limit: typing.Optional[int] = None,
824        name: typing.Optional[str] = None,
825        user_id: typing.Optional[str] = None,
826        trace_id: typing.Optional[str] = None,
827        parent_observation_id: typing.Optional[str] = None,
828        from_start_time: typing.Optional[dt.datetime] = None,
829        to_start_time: typing.Optional[dt.datetime] = None,
830        type: typing.Optional[str] = None,
831    ) -> ObservationsViews:
832        """Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead.
833
834        Args:
835            page (Optional[int]): Page number of the observations to return. Defaults to None.
836            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
837            name (Optional[str]): Name of the observations to return. Defaults to None.
838            user_id (Optional[str]): User identifier. Defaults to None.
839            trace_id (Optional[str]): Trace identifier. Defaults to None.
840            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
841            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
842            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
843            type (Optional[str]): Type of the observation. Defaults to None.
844
845        Returns:
846            List of ObservationsViews: List of observations in the project matching the given parameters.
847
848        Raises:
849            Exception: If an error occurred during the request.
850        """
851        warnings.warn(
852            "get_observations is deprecated, use fetch_observations instead.",
853            DeprecationWarning,
854        )
855        try:
856            self.log.debug(
857                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
858            )
859            return self.client.observations.get_many(
860                page=page,
861                limit=limit,
862                name=name,
863                user_id=user_id,
864                trace_id=trace_id,
865                parent_observation_id=parent_observation_id,
866                from_start_time=from_start_time,
867                to_start_time=to_start_time,
868                type=type,
869            )
870        except Exception as e:
871            handle_fern_exception(e)
872            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:
874    def get_generations(
875        self,
876        *,
877        page: typing.Optional[int] = None,
878        limit: typing.Optional[int] = None,
879        name: typing.Optional[str] = None,
880        user_id: typing.Optional[str] = None,
881        trace_id: typing.Optional[str] = None,
882        from_start_time: typing.Optional[dt.datetime] = None,
883        to_start_time: typing.Optional[dt.datetime] = None,
884        parent_observation_id: typing.Optional[str] = None,
885    ) -> ObservationsViews:
886        """Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead.
887
888        Args:
889            page (Optional[int]): Page number of the generations to return. Defaults to None.
890            limit (Optional[int]): Maximum number of generations to return. Defaults to None.
891            name (Optional[str]): Name of the generations to return. Defaults to None.
892            user_id (Optional[str]): User identifier of the generations to return. Defaults to None.
893            trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None.
894            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
895            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
896            parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None.
897
898        Returns:
899            List of ObservationsViews: List of generations in the project matching the given parameters.
900
901        Raises:
902            Exception: If an error occurred during the request.
903        """
904        warnings.warn(
905            "get_generations is deprecated, use `fetch_observations(type='GENERATION')` instead.",
906            DeprecationWarning,
907        )
908        return self.get_observations(
909            page=page,
910            limit=limit,
911            name=name,
912            user_id=user_id,
913            trace_id=trace_id,
914            parent_observation_id=parent_observation_id,
915            from_start_time=from_start_time,
916            to_start_time=to_start_time,
917            type="GENERATION",
918        )

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

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

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

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

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

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

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

Base class for handling stateful operations in the Langfuse system.

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

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

Initialize the StatefulClient.

Arguments:
  • client (FernLangfuse): Core interface for Langfuse API interactions.
  • id (str): Unique identifier of the stateful client (either observation or trace).
  • state_type (StateType): Enum indicating whether the client is an observation or a trace.
  • trace_id (str): Id of the trace associated with the stateful client.
  • task_manager (TaskManager): Manager handling asynchronous tasks for the client.
log = <Logger langfuse (WARNING)>
client
trace_id
id
state_type
task_manager
environment
def generation( self, *, id: Optional[str] = None, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, input: Optional[Any] = None, output: Optional[Any] = None, usage: Union[pydantic.v1.main.BaseModel, langfuse.model.ModelUsage, NoneType] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None, **kwargs) -> StatefulGenerationClient:
2204    def generation(
2205        self,
2206        *,
2207        id: typing.Optional[str] = None,
2208        name: typing.Optional[str] = None,
2209        start_time: typing.Optional[dt.datetime] = None,
2210        end_time: typing.Optional[dt.datetime] = None,
2211        metadata: typing.Optional[typing.Any] = None,
2212        level: typing.Optional[SpanLevel] = None,
2213        status_message: typing.Optional[str] = None,
2214        version: typing.Optional[str] = None,
2215        completion_start_time: typing.Optional[dt.datetime] = None,
2216        model: typing.Optional[str] = None,
2217        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2218        input: typing.Optional[typing.Any] = None,
2219        output: typing.Optional[typing.Any] = None,
2220        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2221        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2222        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2223        prompt: typing.Optional[PromptClient] = None,
2224        **kwargs,
2225    ) -> "StatefulGenerationClient":
2226        """Create a generation nested within the current observation or trace.
2227
2228        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.
2229
2230        Args:
2231            id (Optional[str]): The id of the generation can be set, defaults to random id.
2232            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2233            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2234            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2235            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.
2236            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2237            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.
2238            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.
2239            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2240            model (Optional[str]): The name of the model used for the generation.
2241            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2242            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2243            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2244            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.
2245            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}.
2246            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}.
2247            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2248            **kwargs: Additional keyword arguments to include in the generation.
2249
2250        Returns:
2251            StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.
2252
2253        Example:
2254            ```python
2255            from langfuse import Langfuse
2256
2257            langfuse = Langfuse()
2258
2259            # Create a trace
2260            trace = langfuse.trace(name = "llm-feature")
2261
2262            # Create a nested generation in Langfuse
2263            generation = trace.generation(
2264                name="summary-generation",
2265                model="gpt-3.5-turbo",
2266                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
2267                input=[{"role": "system", "content": "You are a helpful assistant."},
2268                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
2269                metadata={"interface": "whatsapp"}
2270            )
2271            ```
2272        """
2273        generation_id = id or str(uuid.uuid4())
2274        try:
2275            generation_body = {
2276                "id": generation_id,
2277                "name": name,
2278                "start_time": start_time or _get_timestamp(),
2279                "metadata": metadata,
2280                "level": level,
2281                "status_message": status_message,
2282                "version": version,
2283                "end_time": end_time,
2284                "completion_start_time": completion_start_time,
2285                "model": model,
2286                "model_parameters": model_parameters,
2287                "input": input,
2288                "output": output,
2289                "usage": _convert_usage_input(usage) if usage is not None else None,
2290                "usage_details": usage_details,
2291                "cost_details": cost_details,
2292                "environment": self.environment,
2293                **_create_prompt_context(prompt),
2294                **kwargs,
2295            }
2296
2297            generation_body = self._add_state_to_event(generation_body)
2298            new_body = self._add_default_values(generation_body)
2299
2300            new_body = CreateGenerationBody(**new_body)
2301
2302            event = {
2303                "id": str(uuid.uuid4()),
2304                "type": "generation-create",
2305                "body": new_body.dict(exclude_none=True, exclude_unset=False),
2306            }
2307
2308            self.log.debug(
2309                f"Creating generation {_filter_io_from_event_body(generation_body)}..."
2310            )
2311            self.task_manager.add_task(event)
2312
2313        except Exception as e:
2314            self.log.exception(e)
2315        finally:
2316            return StatefulGenerationClient(
2317                self.client,
2318                generation_id,
2319                StateType.OBSERVATION,
2320                self.trace_id,
2321                self.task_manager,
2322                self.environment,
2323            )

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:
2325    def span(
2326        self,
2327        *,
2328        id: typing.Optional[str] = None,
2329        name: typing.Optional[str] = None,
2330        start_time: typing.Optional[dt.datetime] = None,
2331        end_time: typing.Optional[dt.datetime] = None,
2332        metadata: typing.Optional[typing.Any] = None,
2333        input: typing.Optional[typing.Any] = None,
2334        output: typing.Optional[typing.Any] = None,
2335        level: typing.Optional[SpanLevel] = None,
2336        status_message: typing.Optional[str] = None,
2337        version: typing.Optional[str] = None,
2338        **kwargs,
2339    ) -> "StatefulSpanClient":
2340        """Create a span nested within the current observation or trace.
2341
2342        A span represents durations of units of work in a trace.
2343
2344        Args:
2345            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
2346            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2347            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2348            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2349            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2350            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.
2351            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.
2352            input (Optional[dict]): The input to the span. Can be any JSON object.
2353            output (Optional[dict]): The output to the span. Can be any JSON object.
2354            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2355            **kwargs: Additional keyword arguments to include in the span.
2356
2357        Returns:
2358            StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.
2359
2360        Example:
2361            ```python
2362            from langfuse import Langfuse
2363
2364            langfuse = Langfuse()
2365
2366            # Create a trace
2367            trace = langfuse.trace(name = "llm-feature")
2368
2369            # Create a span
2370            retrieval = langfuse.span(name = "retrieval")
2371            ```
2372        """
2373        span_id = id or str(uuid.uuid4())
2374        try:
2375            span_body = {
2376                "id": span_id,
2377                "name": name,
2378                "start_time": start_time or _get_timestamp(),
2379                "metadata": metadata,
2380                "input": input,
2381                "output": output,
2382                "level": level,
2383                "status_message": status_message,
2384                "version": version,
2385                "end_time": end_time,
2386                "environment": self.environment,
2387                **kwargs,
2388            }
2389
2390            self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...")
2391
2392            new_dict = self._add_state_to_event(span_body)
2393            new_body = self._add_default_values(new_dict)
2394
2395            event = CreateSpanBody(**new_body)
2396
2397            event = {
2398                "id": str(uuid.uuid4()),
2399                "type": "span-create",
2400                "body": event,
2401            }
2402
2403            self.task_manager.add_task(event)
2404        except Exception as e:
2405            self.log.exception(e)
2406        finally:
2407            return StatefulSpanClient(
2408                self.client,
2409                span_id,
2410                StateType.OBSERVATION,
2411                self.trace_id,
2412                self.task_manager,
2413                self.environment,
2414            )

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

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

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

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

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

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

Initialize the StatefulGenerationClient.

log = <Logger langfuse (WARNING)>
def update( self, *, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, completion_start_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, input: Optional[Any] = None, output: Optional[Any] = None, usage: Union[pydantic.v1.main.BaseModel, langfuse.model.ModelUsage, NoneType] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None, **kwargs) -> StatefulGenerationClient:
2652    def update(
2653        self,
2654        *,
2655        name: typing.Optional[str] = None,
2656        start_time: typing.Optional[dt.datetime] = None,
2657        end_time: typing.Optional[dt.datetime] = None,
2658        completion_start_time: typing.Optional[dt.datetime] = None,
2659        metadata: typing.Optional[typing.Any] = None,
2660        level: typing.Optional[SpanLevel] = None,
2661        status_message: typing.Optional[str] = None,
2662        version: typing.Optional[str] = None,
2663        model: typing.Optional[str] = None,
2664        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2665        input: typing.Optional[typing.Any] = None,
2666        output: typing.Optional[typing.Any] = None,
2667        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2668        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2669        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2670        prompt: typing.Optional[PromptClient] = None,
2671        **kwargs,
2672    ) -> "StatefulGenerationClient":
2673        """Update the generation.
2674
2675        Args:
2676            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2677            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2678            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2679            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.
2680            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2681            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.
2682            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.
2683            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2684            model (Optional[str]): The name of the model used for the generation.
2685            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2686            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2687            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2688            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.
2689            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}.
2690            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}.
2691            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2692            **kwargs: Additional keyword arguments to include in the generation.
2693
2694        Returns:
2695            StatefulGenerationClient: The updated generation. Passthrough for chaining.
2696
2697        Example:
2698            ```python
2699            from langfuse import Langfuse
2700
2701            langfuse = Langfuse()
2702
2703            # Create a trace
2704            trace = langfuse.trace(name = "llm-feature")
2705
2706            # Create a nested generation in Langfuse
2707            generation = trace.generation(name="summary-generation")
2708
2709            # Update the generation
2710            generation = generation.update(metadata={"interface": "whatsapp"})
2711            ```
2712        """
2713        try:
2714            generation_body = {
2715                "id": self.id,
2716                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2717                "name": name,
2718                "start_time": start_time,
2719                "metadata": metadata,
2720                "level": level,
2721                "status_message": status_message,
2722                "version": version,
2723                "end_time": end_time,
2724                "completion_start_time": completion_start_time,
2725                "model": model,
2726                "model_parameters": model_parameters,
2727                "input": input,
2728                "output": output,
2729                "usage": _convert_usage_input(usage) if usage is not None else None,
2730                "usage_details": usage_details,
2731                "cost_details": cost_details,
2732                **_create_prompt_context(prompt),
2733                **kwargs,
2734            }
2735
2736            self.log.debug(
2737                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2738            )
2739
2740            request = UpdateGenerationBody(**generation_body)
2741
2742            event = {
2743                "id": str(uuid.uuid4()),
2744                "type": "generation-update",
2745                "body": request.dict(exclude_none=True, exclude_unset=False),
2746            }
2747
2748            self.log.debug(
2749                f"Update generation {_filter_io_from_event_body(generation_body)}..."
2750            )
2751            self.task_manager.add_task(event)
2752
2753        except Exception as e:
2754            self.log.exception(e)
2755        finally:
2756            return StatefulGenerationClient(
2757                self.client,
2758                self.id,
2759                StateType.OBSERVATION,
2760                self.trace_id,
2761                self.task_manager,
2762                self.environment,
2763            )

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:
2765    def end(
2766        self,
2767        *,
2768        name: typing.Optional[str] = None,
2769        start_time: typing.Optional[dt.datetime] = None,
2770        end_time: typing.Optional[dt.datetime] = None,
2771        completion_start_time: typing.Optional[dt.datetime] = None,
2772        metadata: typing.Optional[typing.Any] = None,
2773        level: typing.Optional[SpanLevel] = None,
2774        status_message: typing.Optional[str] = None,
2775        version: typing.Optional[str] = None,
2776        model: typing.Optional[str] = None,
2777        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2778        input: typing.Optional[typing.Any] = None,
2779        output: typing.Optional[typing.Any] = None,
2780        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2781        usage_details: typing.Optional[typing.Dict[str, int]] = None,
2782        cost_details: typing.Optional[typing.Dict[str, float]] = None,
2783        prompt: typing.Optional[PromptClient] = None,
2784        **kwargs,
2785    ) -> "StatefulGenerationClient":
2786        """End the generation, optionally updating its properties.
2787
2788        Args:
2789            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2790            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2791            end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
2792            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.
2793            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2794            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.
2795            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.
2796            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2797            model (Optional[str]): The name of the model used for the generation.
2798            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2799            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2800            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2801            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.
2802            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}.
2803            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}.
2804            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2805            **kwargs: Additional keyword arguments to include in the generation.
2806
2807        Returns:
2808            StatefulGenerationClient: The ended generation. Passthrough for chaining.
2809
2810        Example:
2811            ```python
2812            from langfuse import Langfuse
2813
2814            langfuse = Langfuse()
2815
2816            # Create a trace
2817            trace = langfuse.trace(name = "llm-feature")
2818
2819            # Create a nested generation in Langfuse
2820            generation = trace.generation(name="summary-generation")
2821
2822            # End the generation and update its properties
2823            generation = generation.end(metadata={"interface": "whatsapp"})
2824            ```
2825        """
2826        return self.update(
2827            name=name,
2828            start_time=start_time,
2829            end_time=end_time or _get_timestamp(),
2830            metadata=metadata,
2831            level=level,
2832            status_message=status_message,
2833            version=version,
2834            completion_start_time=completion_start_time,
2835            model=model,
2836            model_parameters=model_parameters,
2837            input=input,
2838            output=output,
2839            usage=usage,
2840            usage_details=usage_details,
2841            cost_details=cost_details,
2842            prompt=prompt,
2843            **kwargs,
2844        )

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

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

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

Initialize the StatefulSpanClient.

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

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

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):
3032    def get_langchain_handler(self, update_parent: bool = False):
3033        """Get langchain callback handler associated with the current span.
3034
3035        Args:
3036            update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
3037
3038        Returns:
3039            CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
3040        """
3041        from langfuse.callback import CallbackHandler
3042
3043        return CallbackHandler(
3044            stateful_client=self, update_stateful_client=update_parent
3045        )

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

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

Attributes:
  • client (FernLangfuse): Core interface for Langfuse API interaction.
  • id (str): Unique identifier of the trace.
  • state_type (StateType): Type of the stateful entity (observation or trace).
  • trace_id (str): The trace ID associated with this client.
  • task_manager (TaskManager): Manager for handling asynchronous tasks.
StatefulTraceClient( client: langfuse.api.client.FernLangfuse, id: str, state_type: StateType, trace_id: str, task_manager: langfuse._task_manager.task_manager.TaskManager, environment: Optional[str] = None)
3061    def __init__(
3062        self,
3063        client: FernLangfuse,
3064        id: str,
3065        state_type: StateType,
3066        trace_id: str,
3067        task_manager: TaskManager,
3068        environment: Optional[str] = None,
3069    ):
3070        """Initialize the StatefulTraceClient."""
3071        super().__init__(client, id, state_type, trace_id, task_manager, environment)
3072        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:
3074    def update(
3075        self,
3076        *,
3077        name: typing.Optional[str] = None,
3078        user_id: typing.Optional[str] = None,
3079        session_id: typing.Optional[str] = None,
3080        version: typing.Optional[str] = None,
3081        release: typing.Optional[str] = None,
3082        input: typing.Optional[typing.Any] = None,
3083        output: typing.Optional[typing.Any] = None,
3084        metadata: typing.Optional[typing.Any] = None,
3085        tags: typing.Optional[typing.List[str]] = None,
3086        public: typing.Optional[bool] = None,
3087        **kwargs,
3088    ) -> "StatefulTraceClient":
3089        """Update the trace.
3090
3091        Args:
3092            name: Identifier of the trace. Useful for sorting/filtering in the UI.
3093            input: The input of the trace. Can be any JSON object.
3094            output: The output of the trace. Can be any JSON object.
3095            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
3096            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
3097            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
3098            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
3099            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
3100            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.
3101            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.
3102            **kwargs: Additional keyword arguments that can be included in the trace.
3103
3104        Returns:
3105            StatefulTraceClient: The updated trace. Passthrough for chaining.
3106
3107        Example:
3108            ```python
3109            from langfuse import Langfuse
3110
3111            langfuse = Langfuse()
3112
3113            # Create a trace
3114            trace = langfuse.trace(
3115                name="example-application",
3116                user_id="user-1234")
3117            )
3118
3119            # Update the trace
3120            trace = trace.update(
3121                output={"result": "success"},
3122                metadata={"interface": "whatsapp"}
3123            )
3124            ```
3125        """
3126        try:
3127            trace_body = {
3128                "id": self.id,
3129                "name": name,
3130                "userId": user_id,
3131                "sessionId": session_id
3132                or kwargs.get("sessionId", None),  # backward compatibility
3133                "version": version,
3134                "release": release,
3135                "input": input,
3136                "output": output,
3137                "metadata": metadata,
3138                "public": public,
3139                "tags": tags,
3140                **kwargs,
3141            }
3142            self.log.debug(f"Update trace {_filter_io_from_event_body(trace_body)}...")
3143
3144            request = TraceBody(**trace_body)
3145
3146            event = {
3147                "id": str(uuid.uuid4()),
3148                "type": "trace-create",
3149                "body": request,
3150            }
3151
3152            self.task_manager.add_task(event)
3153
3154        except Exception as e:
3155            self.log.exception(e)
3156        finally:
3157            return StatefulTraceClient(
3158                self.client,
3159                self.id,
3160                StateType.TRACE,
3161                self.trace_id,
3162                self.task_manager,
3163                self.environment,
3164            )

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):
3166    def get_langchain_handler(self, update_parent: bool = False):
3167        """Get langchain callback handler associated with the current trace.
3168
3169        This method creates and returns a CallbackHandler instance, linking it with the current
3170        trace. Use this if you want to group multiple Langchain runs within a single trace.
3171
3172        Args:
3173            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
3174
3175        Raises:
3176            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
3177
3178        Returns:
3179            CallbackHandler: Langchain callback handler linked to the current trace.
3180
3181        Example:
3182            ```python
3183            from langfuse import Langfuse
3184
3185            langfuse = Langfuse()
3186
3187            # Create a trace
3188            trace = langfuse.trace(name = "llm-feature")
3189
3190            # Get a langchain callback handler
3191            handler = trace.get_langchain_handler()
3192            ```
3193        """
3194        try:
3195            from langfuse.callback import CallbackHandler
3196
3197            self.log.debug(f"Creating new handler for trace {self.id}")
3198
3199            return CallbackHandler(
3200                stateful_client=self,
3201                debug=self.log.level == logging.DEBUG,
3202                update_stateful_client=update_parent,
3203            )
3204        except Exception as e:
3205            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):
3207    def getNewHandler(self):
3208        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
3209        return self.get_langchain_handler()

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

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

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]] = {}):
3421    @contextmanager
3422    def observe_llama_index(
3423        self,
3424        *,
3425        run_name: str,
3426        run_description: Optional[str] = None,
3427        run_metadata: Optional[Any] = None,
3428        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3429    ):
3430        """Context manager for observing LlamaIndex operations linked to this dataset item.
3431
3432        This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging
3433        and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all
3434        operations performed within the context are linked to the appropriate dataset item and run in Langfuse.
3435
3436        Args:
3437            run_name (str): The name of the dataset run.
3438            run_description (Optional[str]): Description of the dataset run. Defaults to None.
3439            run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
3440            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass
3441                to the LlamaIndex integration constructor. Defaults to an empty dictionary.
3442
3443        Yields:
3444            LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.
3445
3446        Example:
3447            ```python
3448            dataset_item = dataset.items[0]
3449
3450            with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler:
3451                # Perform LlamaIndex operations here
3452                some_llama_index_operation()
3453            ```
3454
3455        Raises:
3456            ImportError: If required modules for LlamaIndex integration are not available.
3457        """
3458        metadata = {
3459            "dataset_item_id": self.id,
3460            "run_name": run_name,
3461            "dataset_id": self.dataset_id,
3462        }
3463        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3464        self.link(
3465            trace, run_name, run_metadata=run_metadata, run_description=run_description
3466        )
3467
3468        try:
3469            import llama_index.core
3470            from llama_index.core import Settings
3471            from llama_index.core.callbacks import CallbackManager
3472
3473            from langfuse.llama_index import LlamaIndexCallbackHandler
3474
3475            callback_handler = LlamaIndexCallbackHandler(
3476                **llama_index_integration_constructor_kwargs,
3477            )
3478            callback_handler.set_root(trace, update_root=True)
3479
3480            # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler
3481            # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it
3482            prev_global_handler = llama_index.core.global_handler
3483            prev_langfuse_handler = None
3484
3485            if isinstance(prev_global_handler, LlamaIndexCallbackHandler):
3486                llama_index.core.global_handler = None
3487
3488            if Settings.callback_manager is None:
3489                Settings.callback_manager = CallbackManager([callback_handler])
3490            else:
3491                for handler in Settings.callback_manager.handlers:
3492                    if isinstance(handler, LlamaIndexCallbackHandler):
3493                        prev_langfuse_handler = handler
3494                        Settings.callback_manager.remove_handler(handler)
3495
3496                Settings.callback_manager.add_handler(callback_handler)
3497
3498        except Exception as e:
3499            self.log.exception(e)
3500
3501        try:
3502            yield callback_handler
3503        finally:
3504            # Reset the handlers
3505            Settings.callback_manager.remove_handler(callback_handler)
3506            if prev_langfuse_handler is not None:
3507                Settings.callback_manager.add_handler(prev_langfuse_handler)
3508
3509            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]] = {}):
3511    def get_llama_index_handler(
3512        self,
3513        *,
3514        run_name: str,
3515        run_description: Optional[str] = None,
3516        run_metadata: Optional[Any] = None,
3517        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3518    ):
3519        """Create and get a llama-index callback handler linked to this dataset item.
3520
3521        Args:
3522            run_name (str): The name of the dataset run to be used in the callback handler.
3523            run_description (Optional[str]): Description of the dataset run.
3524            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3525            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
3526
3527        Returns:
3528            LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.
3529        """
3530        metadata = {
3531            "dataset_item_id": self.id,
3532            "run_name": run_name,
3533            "dataset_id": self.dataset_id,
3534        }
3535        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3536
3537        self.link(
3538            trace, run_name, run_metadata=run_metadata, run_description=run_description
3539        )
3540
3541        try:
3542            from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler
3543
3544            callback_handler = LlamaIndexCallbackHandler(
3545                **llama_index_integration_constructor_kwargs,
3546            )
3547            callback_handler.set_root(trace, update_root=True)
3548
3549            return callback_handler
3550        except Exception as e:
3551            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:
3554class DatasetClient:
3555    """Class for managing datasets in Langfuse.
3556
3557    Attributes:
3558        id (str): Unique identifier of the dataset.
3559        name (str): Name of the dataset.
3560        description (Optional[str]): Description of the dataset.
3561        metadata (Optional[typing.Any]): Additional metadata of the dataset.
3562        project_id (str): Identifier of the project to which the dataset belongs.
3563        dataset_name (str): Name of the dataset.
3564        created_at (datetime): Timestamp of dataset creation.
3565        updated_at (datetime): Timestamp of the last update to the dataset.
3566        items (List[DatasetItemClient]): List of dataset items associated with the dataset.
3567        runs (List[str]): List of dataset runs associated with the dataset. Deprecated.
3568
3569    Example:
3570        Print the input of each dataset item in a dataset.
3571        ```python
3572        from langfuse import Langfuse
3573
3574        langfuse = Langfuse()
3575
3576        dataset = langfuse.get_dataset("<dataset_name>")
3577
3578        for item in dataset.items:
3579            print(item.input)
3580        ```
3581    """
3582
3583    id: str
3584    name: str
3585    description: Optional[str]
3586    project_id: str
3587    dataset_name: str  # for backward compatibility, to be deprecated
3588    metadata: Optional[Any]
3589    created_at: dt.datetime
3590    updated_at: dt.datetime
3591    items: typing.List[DatasetItemClient]
3592    runs: typing.List[str] = []  # deprecated
3593
3594    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3595        """Initialize the DatasetClient."""
3596        self.id = dataset.id
3597        self.name = dataset.name
3598        self.description = dataset.description
3599        self.project_id = dataset.project_id
3600        self.metadata = dataset.metadata
3601        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3602        self.created_at = dataset.created_at
3603        self.updated_at = dataset.updated_at
3604        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])
3594    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3595        """Initialize the DatasetClient."""
3596        self.id = dataset.id
3597        self.name = dataset.name
3598        self.description = dataset.description
3599        self.project_id = dataset.project_id
3600        self.metadata = dataset.metadata
3601        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3602        self.created_at = dataset.created_at
3603        self.updated_at = dataset.updated_at
3604        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] = []