langfuse.client

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

Response object for fetch_trace method.

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

Response object for fetch_observation method.

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

enabled
base_url
httpx_client
client
task_manager
trace_id
release
prompt_cache
def get_trace_id(self) -> str:
331    def get_trace_id(self) -> str:
332        """Get the current trace id."""
333        return self.trace_id

Get the current trace id.

def get_trace_url(self) -> str:
335    def get_trace_url(self) -> str:
336        """Get the URL of the current trace to view it in the Langfuse UI."""
337        return f"{self.base_url}/trace/{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:
339    def get_dataset(
340        self, name: str, *, fetch_items_page_size: Optional[int] = 50
341    ) -> "DatasetClient":
342        """Fetch a dataset by its name.
343
344        Args:
345            name (str): The name of the dataset to fetch.
346            fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
347
348        Returns:
349            DatasetClient: The dataset with the given name.
350        """
351        try:
352            self.log.debug(f"Getting datasets {name}")
353            dataset = self.client.datasets.get(dataset_name=name)
354
355            dataset_items = []
356            page = 1
357            while True:
358                new_items = self.client.dataset_items.list(
359                    dataset_name=name, page=page, limit=fetch_items_page_size
360                )
361                dataset_items.extend(new_items.data)
362                if new_items.meta.total_pages <= page:
363                    break
364                page += 1
365
366            items = [DatasetItemClient(i, langfuse=self) for i in dataset_items]
367
368            return DatasetClient(dataset, items=items)
369        except Exception as e:
370            self.log.exception(e)
371            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:
373    def get_dataset_item(self, id: str) -> "DatasetItemClient":
374        """Get the dataset item with the given id."""
375        try:
376            self.log.debug(f"Getting dataset item {id}")
377            dataset_item = self.client.dataset_items.get(id=id)
378            return DatasetItemClient(dataset_item, langfuse=self)
379        except Exception as e:
380            self.log.exception(e)
381            raise e

Get the dataset item with the given id.

def auth_check(self) -> bool:
383    def auth_check(self) -> bool:
384        """Check if the provided credentials (public and secret key) are valid.
385
386        Raises:
387            Exception: If no projects were found for the provided credentials.
388
389        Note:
390            This method is blocking. It is discouraged to use it in production code.
391        """
392        try:
393            projects = self.client.projects.get()
394            self.log.debug(
395                f"Auth check successful, found {len(projects.data)} projects"
396            )
397            if len(projects.data) == 0:
398                raise Exception(
399                    "Auth check failed, no project found for the keys provided."
400                )
401            return True
402
403        except Exception as e:
404            self.log.exception(e)
405            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:
407    def get_dataset_runs(
408        self,
409        dataset_name: str,
410        *,
411        page: typing.Optional[int] = None,
412        limit: typing.Optional[int] = None,
413    ) -> PaginatedDatasetRuns:
414        """Get all dataset runs.
415
416        Args:
417            dataset_name (str): Name of the dataset.
418            page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None.
419            limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50.
420
421        Returns:
422            PaginatedDatasetRuns: The dataset runs.
423        """
424        try:
425            self.log.debug("Getting dataset runs")
426            return self.client.datasets.get_runs(
427                dataset_name=dataset_name, page=page, limit=limit
428            )
429        except Exception as e:
430            self.log.exception(e)
431            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:
433    def get_dataset_run(
434        self,
435        dataset_name: str,
436        dataset_run_name: str,
437    ) -> DatasetRunWithItems:
438        """Get a dataset run.
439
440        Args:
441            dataset_name: Name of the dataset.
442            dataset_run_name: Name of the dataset run.
443
444        Returns:
445            DatasetRunWithItems: The dataset run.
446        """
447        try:
448            self.log.debug(
449                f"Getting dataset runs for dataset {dataset_name} and run {dataset_run_name}"
450            )
451            return self.client.datasets.get_run(
452                dataset_name=dataset_name, run_name=dataset_run_name
453            )
454        except Exception as e:
455            self.log.exception(e)
456            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:
458    def create_dataset(
459        self,
460        name: str,
461        description: Optional[str] = None,
462        metadata: Optional[Any] = None,
463    ) -> Dataset:
464        """Create a dataset with the given name on Langfuse.
465
466        Args:
467            name: Name of the dataset to create.
468            description: Description of the dataset. Defaults to None.
469            metadata: Additional metadata. Defaults to None.
470
471        Returns:
472            Dataset: The created dataset as returned by the Langfuse API.
473        """
474        try:
475            body = CreateDatasetRequest(
476                name=name, description=description, metadata=metadata
477            )
478            self.log.debug(f"Creating datasets {body}")
479            return self.client.datasets.create(request=body)
480        except Exception as e:
481            self.log.exception(e)
482            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:
484    def create_dataset_item(
485        self,
486        dataset_name: str,
487        input: Optional[Any] = None,
488        expected_output: Optional[Any] = None,
489        metadata: Optional[Any] = None,
490        source_trace_id: Optional[str] = None,
491        source_observation_id: Optional[str] = None,
492        status: Optional[DatasetStatus] = None,
493        id: Optional[str] = None,
494    ) -> DatasetItem:
495        """Create a dataset item.
496
497        Upserts if an item with id already exists.
498
499        Args:
500            dataset_name: Name of the dataset in which the dataset item should be created.
501            input: Input data. Defaults to None. Can contain any dict, list or scalar.
502            expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
503            metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
504            source_trace_id: Id of the source trace. Defaults to None.
505            source_observation_id: Id of the source observation. Defaults to None.
506            status: Status of the dataset item. Defaults to ACTIVE for newly created items.
507            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.
508
509        Returns:
510            DatasetItem: The created dataset item as returned by the Langfuse API.
511
512        Example:
513            ```python
514            from langfuse import Langfuse
515
516            langfuse = Langfuse()
517
518            # Uploading items to the Langfuse dataset named "capital_cities"
519            langfuse.create_dataset_item(
520                dataset_name="capital_cities",
521                input={"input": {"country": "Italy"}},
522                expected_output={"expected_output": "Rome"},
523                metadata={"foo": "bar"}
524            )
525            ```
526        """
527        try:
528            body = CreateDatasetItemRequest(
529                datasetName=dataset_name,
530                input=input,
531                expectedOutput=expected_output,
532                metadata=metadata,
533                sourceTraceId=source_trace_id,
534                sourceObservationId=source_observation_id,
535                status=status,
536                id=id,
537            )
538            self.log.debug(f"Creating dataset item {body}")
539            return self.client.dataset_items.create(request=body)
540        except Exception as e:
541            self.log.exception(e)
542            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:
544    def fetch_trace(
545        self,
546        id: str,
547    ) -> FetchTraceResponse:
548        """Fetch a trace via the Langfuse API by its id.
549
550        Args:
551            id: The id of the trace to fetch.
552
553        Returns:
554            FetchTraceResponse: The trace with full details as returned by the Langfuse API on `data`.
555
556        Raises:
557            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
558        """
559        try:
560            self.log.debug(f"Getting trace {id}")
561            trace = self.client.trace.get(id)
562            return FetchTraceResponse(data=trace)
563        except Exception as e:
564            self.log.exception(e)
565            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:
567    def get_trace(
568        self,
569        id: str,
570    ) -> TraceWithFullDetails:
571        """Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead.
572
573        Args:
574            id: The id of the trace to fetch.
575
576        Returns:
577            TraceWithFullDetails: The trace with full details as returned by the Langfuse API.
578
579        Raises:
580            Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
581        """
582        warnings.warn(
583            "get_trace is deprecated, use fetch_trace instead.",
584            DeprecationWarning,
585        )
586
587        try:
588            self.log.debug(f"Getting trace {id}")
589            return self.client.trace.get(id)
590        except Exception as e:
591            self.log.exception(e)
592            raise e

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

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

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

Raises:
  • Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
def fetch_traces( self, *, page: Optional[int] = None, limit: Optional[int] = None, user_id: Optional[str] = None, name: Optional[str] = None, session_id: Optional[str] = None, from_timestamp: Optional[datetime.datetime] = None, to_timestamp: Optional[datetime.datetime] = None, order_by: Optional[str] = None, tags: Union[str, Sequence[str], NoneType] = None) -> FetchTracesResponse:
594    def fetch_traces(
595        self,
596        *,
597        page: Optional[int] = None,
598        limit: Optional[int] = None,
599        user_id: Optional[str] = None,
600        name: Optional[str] = None,
601        session_id: Optional[str] = None,
602        from_timestamp: Optional[dt.datetime] = None,
603        to_timestamp: Optional[dt.datetime] = None,
604        order_by: Optional[str] = None,
605        tags: Optional[Union[str, Sequence[str]]] = None,
606    ) -> FetchTracesResponse:
607        """Fetch a list of traces in the current project matching the given parameters.
608
609        Args:
610            page (Optional[int]): Page number, starts at 1. Defaults to None.
611            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.
612            name (Optional[str]): Filter by name of traces. Defaults to None.
613            user_id (Optional[str]): Filter by user_id. Defaults to None.
614            session_id (Optional[str]): Filter by session_id. Defaults to None.
615            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
616            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
617            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.
618            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
619
620        Returns:
621            FetchTracesResponse, list of traces on `data` and metadata on `meta`.
622
623        Raises:
624            Exception: If an error occurred during the request.
625        """
626        try:
627            self.log.debug(
628                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
629            )
630            res = self.client.trace.list(
631                page=page,
632                limit=limit,
633                name=name,
634                user_id=user_id,
635                session_id=session_id,
636                from_timestamp=from_timestamp,
637                to_timestamp=to_timestamp,
638                order_by=order_by,
639                tags=tags,
640            )
641            return FetchTracesResponse(data=res.data, meta=res.meta)
642        except Exception as e:
643            self.log.exception(e)
644            raise e

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

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

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

Raises:
  • Exception: If an error occurred during the request.
def get_traces( self, *, page: Optional[int] = None, limit: Optional[int] = None, user_id: Optional[str] = None, name: Optional[str] = None, session_id: Optional[str] = None, from_timestamp: Optional[datetime.datetime] = None, to_timestamp: Optional[datetime.datetime] = None, order_by: Optional[str] = None, tags: Union[str, Sequence[str], NoneType] = None) -> langfuse.api.Traces:
646    def get_traces(
647        self,
648        *,
649        page: Optional[int] = None,
650        limit: Optional[int] = None,
651        user_id: Optional[str] = None,
652        name: Optional[str] = None,
653        session_id: Optional[str] = None,
654        from_timestamp: Optional[dt.datetime] = None,
655        to_timestamp: Optional[dt.datetime] = None,
656        order_by: Optional[str] = None,
657        tags: Optional[Union[str, Sequence[str]]] = None,
658    ) -> Traces:
659        """Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead.
660
661        Args:
662            page (Optional[int]): Page number, starts at 1. Defaults to None.
663            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.
664            name (Optional[str]): Filter by name of traces. Defaults to None.
665            user_id (Optional[str]): Filter by user_id. Defaults to None.
666            session_id (Optional[str]): Filter by session_id. Defaults to None.
667            from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
668            to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
669            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.
670            tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
671
672        Returns:
673            List of Traces
674
675        Raises:
676            Exception: If an error occurred during the request.
677        """
678        warnings.warn(
679            "get_traces is deprecated, use fetch_traces instead.",
680            DeprecationWarning,
681        )
682        try:
683            self.log.debug(
684                f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}"
685            )
686            return self.client.trace.list(
687                page=page,
688                limit=limit,
689                name=name,
690                user_id=user_id,
691                session_id=session_id,
692                from_timestamp=from_timestamp,
693                to_timestamp=to_timestamp,
694                order_by=order_by,
695                tags=tags,
696            )
697        except Exception as e:
698            self.log.exception(e)
699            raise e

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

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

List of Traces

Raises:
  • Exception: If an error occurred during the request.
def fetch_observations( self, *, page: Optional[int] = None, limit: Optional[int] = None, name: Optional[str] = None, user_id: Optional[str] = None, trace_id: Optional[str] = None, parent_observation_id: Optional[str] = None, from_start_time: Optional[datetime.datetime] = None, to_start_time: Optional[datetime.datetime] = None, type: Optional[str] = None) -> FetchObservationsResponse:
701    def fetch_observations(
702        self,
703        *,
704        page: typing.Optional[int] = None,
705        limit: typing.Optional[int] = None,
706        name: typing.Optional[str] = None,
707        user_id: typing.Optional[str] = None,
708        trace_id: typing.Optional[str] = None,
709        parent_observation_id: typing.Optional[str] = None,
710        from_start_time: typing.Optional[dt.datetime] = None,
711        to_start_time: typing.Optional[dt.datetime] = None,
712        type: typing.Optional[str] = None,
713    ) -> FetchObservationsResponse:
714        """Get a list of observations in the current project matching the given parameters.
715
716        Args:
717            page (Optional[int]): Page number of the observations to return. Defaults to None.
718            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
719            name (Optional[str]): Name of the observations to return. Defaults to None.
720            user_id (Optional[str]): User identifier. Defaults to None.
721            trace_id (Optional[str]): Trace identifier. Defaults to None.
722            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
723            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
724            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
725            type (Optional[str]): Type of the observation. Defaults to None.
726
727        Returns:
728            FetchObservationsResponse, list of observations on `data` and metadata on `meta`.
729
730        Raises:
731            Exception: If an error occurred during the request.
732        """
733        try:
734            self.log.debug(
735                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
736            )
737            res = self.client.observations.get_many(
738                page=page,
739                limit=limit,
740                name=name,
741                user_id=user_id,
742                trace_id=trace_id,
743                parent_observation_id=parent_observation_id,
744                from_start_time=from_start_time,
745                to_start_time=to_start_time,
746                type=type,
747            )
748            return FetchObservationsResponse(data=res.data, meta=res.meta)
749        except Exception as e:
750            self.log.exception(e)
751            raise e

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

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

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

Raises:
  • Exception: If an error occurred during the request.
def get_observations( self, *, page: Optional[int] = None, limit: Optional[int] = None, name: Optional[str] = None, user_id: Optional[str] = None, trace_id: Optional[str] = None, parent_observation_id: Optional[str] = None, from_start_time: Optional[datetime.datetime] = None, to_start_time: Optional[datetime.datetime] = None, type: Optional[str] = None) -> langfuse.api.ObservationsViews:
753    def get_observations(
754        self,
755        *,
756        page: typing.Optional[int] = None,
757        limit: typing.Optional[int] = None,
758        name: typing.Optional[str] = None,
759        user_id: typing.Optional[str] = None,
760        trace_id: typing.Optional[str] = None,
761        parent_observation_id: typing.Optional[str] = None,
762        from_start_time: typing.Optional[dt.datetime] = None,
763        to_start_time: typing.Optional[dt.datetime] = None,
764        type: typing.Optional[str] = None,
765    ) -> ObservationsViews:
766        """Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead.
767
768        Args:
769            page (Optional[int]): Page number of the observations to return. Defaults to None.
770            limit (Optional[int]): Maximum number of observations to return. Defaults to None.
771            name (Optional[str]): Name of the observations to return. Defaults to None.
772            user_id (Optional[str]): User identifier. Defaults to None.
773            trace_id (Optional[str]): Trace identifier. Defaults to None.
774            parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
775            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
776            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
777            type (Optional[str]): Type of the observation. Defaults to None.
778
779        Returns:
780            List of ObservationsViews: List of observations in the project matching the given parameters.
781
782        Raises:
783            Exception: If an error occurred during the request.
784        """
785        warnings.warn(
786            "get_observations is deprecated, use fetch_observations instead.",
787            DeprecationWarning,
788        )
789        try:
790            self.log.debug(
791                f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}"
792            )
793            return self.client.observations.get_many(
794                page=page,
795                limit=limit,
796                name=name,
797                user_id=user_id,
798                trace_id=trace_id,
799                parent_observation_id=parent_observation_id,
800                from_start_time=from_start_time,
801                to_start_time=to_start_time,
802                type=type,
803            )
804        except Exception as e:
805            self.log.exception(e)
806            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:
808    def get_generations(
809        self,
810        *,
811        page: typing.Optional[int] = None,
812        limit: typing.Optional[int] = None,
813        name: typing.Optional[str] = None,
814        user_id: typing.Optional[str] = None,
815        trace_id: typing.Optional[str] = None,
816        from_start_time: typing.Optional[dt.datetime] = None,
817        to_start_time: typing.Optional[dt.datetime] = None,
818        parent_observation_id: typing.Optional[str] = None,
819    ) -> ObservationsViews:
820        """Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead.
821
822        Args:
823            page (Optional[int]): Page number of the generations to return. Defaults to None.
824            limit (Optional[int]): Maximum number of generations to return. Defaults to None.
825            name (Optional[str]): Name of the generations to return. Defaults to None.
826            user_id (Optional[str]): User identifier of the generations to return. Defaults to None.
827            trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None.
828            from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
829            to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
830            parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None.
831
832        Returns:
833            List of ObservationsViews: List of generations in the project matching the given parameters.
834
835        Raises:
836            Exception: If an error occurred during the request.
837        """
838        warnings.warn(
839            "get_generations is deprecated, use `fetch_observations(type='GENERATION')` instead.",
840            DeprecationWarning,
841        )
842        return self.get_observations(
843            page=page,
844            limit=limit,
845            name=name,
846            user_id=user_id,
847            trace_id=trace_id,
848            parent_observation_id=parent_observation_id,
849            from_start_time=from_start_time,
850            to_start_time=to_start_time,
851            type="GENERATION",
852        )

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:
854    def fetch_observation(
855        self,
856        id: str,
857    ) -> FetchObservationResponse:
858        """Get an observation in the current project with the given identifier.
859
860        Args:
861            id: The identifier of the observation to fetch.
862
863        Returns:
864            FetchObservationResponse: The observation with the given id on `data`.
865
866        Raises:
867            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
868        """
869        try:
870            self.log.debug(f"Getting observation {id}")
871            observation = self.client.observations.get(id)
872            return FetchObservationResponse(data=observation)
873        except Exception as e:
874            self.log.exception(e)
875            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 get_observation( self, id: str) -> langfuse.api.Observation:
877    def get_observation(
878        self,
879        id: str,
880    ) -> Observation:
881        """Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead.
882
883        Args:
884            id: The identifier of the observation to fetch.
885
886        Raises:
887            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
888        """
889        warnings.warn(
890            "get_observation is deprecated, use fetch_observation instead.",
891            DeprecationWarning,
892        )
893        try:
894            self.log.debug(f"Getting observation {id}")
895            return self.client.observations.get(id)
896        except Exception as e:
897            self.log.exception(e)
898            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:
900    def fetch_sessions(
901        self,
902        *,
903        page: typing.Optional[int] = None,
904        limit: typing.Optional[int] = None,
905        from_timestamp: typing.Optional[dt.datetime] = None,
906        to_timestamp: typing.Optional[dt.datetime] = None,
907    ) -> FetchSessionsResponse:
908        """Get a list of sessions in the current project.
909
910        Args:
911            page (Optional[int]): Page number of the sessions to return. Defaults to None.
912            limit (Optional[int]): Maximum number of sessions to return. Defaults to None.
913            from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None.
914            to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None.
915
916        Returns:
917            FetchSessionsResponse, list of sessions on `data` and metadata on `meta`.
918
919        Raises:
920            Exception: If an error occurred during the request.
921        """
922        try:
923            self.log.debug(
924                f"Getting sessions... {page}, {limit}, {from_timestamp}, {to_timestamp}"
925            )
926            res = self.client.sessions.list(
927                page=page,
928                limit=limit,
929                from_timestamp=from_timestamp,
930                to_timestamp=to_timestamp,
931            )
932            return FetchSessionsResponse(data=res.data, meta=res.meta)
933        except Exception as e:
934            self.log.exception(e)
935            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]:
 965    def get_prompt(
 966        self,
 967        name: str,
 968        version: Optional[int] = None,
 969        *,
 970        label: Optional[str] = None,
 971        type: Literal["chat", "text"] = "text",
 972        cache_ttl_seconds: Optional[int] = None,
 973        fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None,
 974        max_retries: Optional[int] = None,
 975        fetch_timeout_seconds: Optional[int] = None,
 976    ) -> PromptClient:
 977        """Get a prompt.
 978
 979        This method attempts to fetch the requested prompt from the local cache. If the prompt is not found
 980        in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again
 981        and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will
 982        return the expired prompt as a fallback.
 983
 984        Args:
 985            name (str): The name of the prompt to retrieve.
 986
 987        Keyword Args:
 988            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.
 989            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.
 990            cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a
 991            keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0.
 992            type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text".
 993            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.
 994            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.
 995            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.
 996
 997        Returns:
 998            The prompt object retrieved from the cache or directly fetched if not cached or expired of type
 999            - TextPromptClient, if type argument is 'text'.
1000            - ChatPromptClient, if type argument is 'chat'.
1001
1002        Raises:
1003            Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
1004            expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
1005        """
1006        if version is not None and label is not None:
1007            raise ValueError("Cannot specify both version and label at the same time.")
1008
1009        if not name:
1010            raise ValueError("Prompt name cannot be empty.")
1011
1012        cache_key = PromptCache.generate_cache_key(name, version=version, label=label)
1013        bounded_max_retries = self._get_bounded_max_retries(
1014            max_retries, default_max_retries=2, max_retries_upper_bound=4
1015        )
1016
1017        self.log.debug(f"Getting prompt '{cache_key}'")
1018        cached_prompt = self.prompt_cache.get(cache_key)
1019
1020        if cached_prompt is None or cache_ttl_seconds == 0:
1021            self.log.debug(
1022                f"Prompt '{cache_key}' not found in cache or caching disabled."
1023            )
1024            try:
1025                return self._fetch_prompt_and_update_cache(
1026                    name,
1027                    version=version,
1028                    label=label,
1029                    ttl_seconds=cache_ttl_seconds,
1030                    max_retries=bounded_max_retries,
1031                    fetch_timeout_seconds=fetch_timeout_seconds,
1032                )
1033            except Exception as e:
1034                if fallback:
1035                    self.log.warning(
1036                        f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}"
1037                    )
1038
1039                    fallback_client_args = {
1040                        "name": name,
1041                        "prompt": fallback,
1042                        "type": type,
1043                        "version": version or 0,
1044                        "config": {},
1045                        "labels": [label] if label else [],
1046                        "tags": [],
1047                    }
1048
1049                    if type == "text":
1050                        return TextPromptClient(
1051                            prompt=Prompt_Text(**fallback_client_args),
1052                            is_fallback=True,
1053                        )
1054
1055                    if type == "chat":
1056                        return ChatPromptClient(
1057                            prompt=Prompt_Chat(**fallback_client_args),
1058                            is_fallback=True,
1059                        )
1060
1061                raise e
1062
1063        if cached_prompt.is_expired():
1064            self.log.debug(f"Stale prompt '{cache_key}' found in cache.")
1065            try:
1066                # refresh prompt in background thread, refresh_prompt deduplicates tasks
1067                self.log.debug(f"Refreshing prompt '{cache_key}' in background.")
1068                self.prompt_cache.add_refresh_prompt_task(
1069                    cache_key,
1070                    lambda: self._fetch_prompt_and_update_cache(
1071                        name,
1072                        version=version,
1073                        label=label,
1074                        ttl_seconds=cache_ttl_seconds,
1075                        max_retries=bounded_max_retries,
1076                        fetch_timeout_seconds=fetch_timeout_seconds,
1077                    ),
1078                )
1079                self.log.debug(f"Returning stale prompt '{cache_key}' from cache.")
1080                # return stale prompt
1081                return cached_prompt.value
1082
1083            except Exception as e:
1084                self.log.warning(
1085                    f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}"
1086                )
1087                # creation of refresh prompt task failed, return stale prompt
1088                return cached_prompt.value
1089
1090        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) -> Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient]:
1180    def create_prompt(
1181        self,
1182        *,
1183        name: str,
1184        prompt: Union[str, List[ChatMessageDict]],
1185        is_active: Optional[bool] = None,  # deprecated
1186        labels: List[str] = [],
1187        tags: Optional[List[str]] = None,
1188        type: Optional[Literal["chat", "text"]] = "text",
1189        config: Optional[Any] = None,
1190    ) -> PromptClient:
1191        """Create a new prompt in Langfuse.
1192
1193        Keyword Args:
1194            name : The name of the prompt to be created.
1195            prompt : The content of the prompt to be created.
1196            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.
1197            labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label.
1198            tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt.
1199            config: Additional structured data to be saved with the prompt. Defaults to None.
1200            type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text".
1201
1202        Returns:
1203            TextPromptClient: The prompt if type argument is 'text'.
1204            ChatPromptClient: The prompt if type argument is 'chat'.
1205        """
1206        try:
1207            self.log.debug(f"Creating prompt {name=}, {version=}, {labels=}")
1208
1209            # Handle deprecated is_active flag
1210            if is_active:
1211                self.log.warning(
1212                    "The 'is_active' flag is deprecated and will be removed in a future release. Please use the 'production' label instead."
1213                )
1214
1215                labels = labels if "production" in labels else labels + ["production"]
1216
1217            if type == "chat":
1218                if not isinstance(prompt, list):
1219                    raise ValueError(
1220                        "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes."
1221                    )
1222                request = CreatePromptRequest_Chat(
1223                    name=name,
1224                    prompt=prompt,
1225                    labels=labels,
1226                    tags=tags,
1227                    config=config or {},
1228                    type="chat",
1229                )
1230                server_prompt = self.client.prompts.create(request=request)
1231
1232                return ChatPromptClient(prompt=server_prompt)
1233
1234            if not isinstance(prompt, str):
1235                raise ValueError("For 'text' type, 'prompt' must be a string.")
1236
1237            request = CreatePromptRequest_Text(
1238                name=name,
1239                prompt=prompt,
1240                labels=labels,
1241                tags=tags,
1242                config=config or {},
1243                type="text",
1244            )
1245
1246            server_prompt = self.client.prompts.create(request=request)
1247            return TextPromptClient(prompt=server_prompt)
1248
1249        except Exception as e:
1250            self.log.exception(e)
1251            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".

Returns:

TextPromptClient: The prompt if type argument is 'text'. ChatPromptClient: The prompt if type argument is 'chat'.

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:
1256    def trace(
1257        self,
1258        *,
1259        id: typing.Optional[str] = None,
1260        name: typing.Optional[str] = None,
1261        user_id: typing.Optional[str] = None,
1262        session_id: typing.Optional[str] = None,
1263        version: typing.Optional[str] = None,
1264        input: typing.Optional[typing.Any] = None,
1265        output: typing.Optional[typing.Any] = None,
1266        metadata: typing.Optional[typing.Any] = None,
1267        tags: typing.Optional[typing.List[str]] = None,
1268        timestamp: typing.Optional[dt.datetime] = None,
1269        public: typing.Optional[bool] = None,
1270        **kwargs,
1271    ) -> "StatefulTraceClient":
1272        """Create a trace.
1273
1274        Args:
1275            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.
1276            name: Identifier of the trace. Useful for sorting/filtering in the UI.
1277            input: The input of the trace. Can be any JSON object.
1278            output: The output of the trace. Can be any JSON object.
1279            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
1280            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
1281            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
1282            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
1283            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
1284            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.
1285            timestamp: The timestamp of the trace. Defaults to the current time if not provided.
1286            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.
1287            **kwargs: Additional keyword arguments that can be included in the trace.
1288
1289        Returns:
1290            StatefulTraceClient: The created trace.
1291
1292        Example:
1293            ```python
1294            from langfuse import Langfuse
1295
1296            langfuse = Langfuse()
1297
1298            trace = langfuse.trace(
1299                name="example-application",
1300                user_id="user-1234")
1301            )
1302            ```
1303        """
1304        new_id = id or str(uuid.uuid4())
1305        self.trace_id = new_id
1306        try:
1307            new_dict = {
1308                "id": new_id,
1309                "name": name,
1310                "userId": user_id,
1311                "sessionId": session_id
1312                or kwargs.get("sessionId", None),  # backward compatibility
1313                "release": self.release,
1314                "version": version,
1315                "metadata": metadata,
1316                "input": input,
1317                "output": output,
1318                "tags": tags,
1319                "timestamp": timestamp or _get_timestamp(),
1320                "public": public,
1321            }
1322            if kwargs is not None:
1323                new_dict.update(kwargs)
1324
1325            new_body = TraceBody(**new_dict)
1326
1327            self.log.debug(f"Creating trace {new_body}")
1328            event = {
1329                "id": str(uuid.uuid4()),
1330                "type": "trace-create",
1331                "body": new_body.dict(exclude_none=True),
1332            }
1333
1334            self.task_manager.add_task(
1335                event,
1336            )
1337
1338        except Exception as e:
1339            self.log.exception(e)
1340        finally:
1341            self._log_memory_usage()
1342
1343            return StatefulTraceClient(
1344                self.client, new_id, StateType.TRACE, new_id, self.task_manager
1345            )

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:
1424    def score(
1425        self,
1426        *,
1427        name: str,
1428        value: typing.Union[float, str],
1429        data_type: typing.Optional[ScoreDataType] = None,
1430        trace_id: typing.Optional[str] = None,
1431        id: typing.Optional[str] = None,
1432        comment: typing.Optional[str] = None,
1433        observation_id: typing.Optional[str] = None,
1434        config_id: typing.Optional[str] = None,
1435        **kwargs,
1436    ) -> "StatefulClient":
1437        """Create a score attached to a trace (and optionally an observation).
1438
1439        Args:
1440            name (str): Identifier of the score.
1441            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.
1442            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.
1443              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.
1444            trace_id (str): The id of the trace to which the score should be attached.
1445            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
1446            comment (Optional[str]): Additional context/explanation of the score.
1447            observation_id (Optional[str]): The id of the observation to which the score should be attached.
1448            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
1449            **kwargs: Additional keyword arguments to include in the score.
1450
1451        Returns:
1452            StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided).
1453
1454        Example:
1455            ```python
1456            from langfuse import Langfuse
1457
1458            langfuse = Langfuse()
1459
1460            # Create a trace
1461            trace = langfuse.trace(name="example-application")
1462
1463            # Get id of created trace
1464            trace_id = trace.id
1465
1466            # Add score to the trace
1467            trace = langfuse.score(
1468                trace_id=trace_id,
1469                name="user-explicit-feedback",
1470                value=0.9,
1471                comment="I like how personalized the response is"
1472            )
1473            ```
1474        """
1475        trace_id = trace_id or self.trace_id or str(uuid.uuid4())
1476        new_id = id or str(uuid.uuid4())
1477        try:
1478            new_dict = {
1479                "id": new_id,
1480                "trace_id": trace_id,
1481                "observation_id": observation_id,
1482                "name": name,
1483                "value": value,
1484                "data_type": data_type,
1485                "comment": comment,
1486                "config_id": config_id,
1487                **kwargs,
1488            }
1489
1490            self.log.debug(f"Creating score {new_dict}...")
1491            new_body = ScoreBody(**new_dict)
1492
1493            event = {
1494                "id": str(uuid.uuid4()),
1495                "type": "score-create",
1496                "body": new_body.dict(exclude_none=True),
1497            }
1498            self.task_manager.add_task(event)
1499
1500        except Exception as e:
1501            self.log.exception(e)
1502        finally:
1503            if observation_id is not None:
1504                return StatefulClient(
1505                    self.client,
1506                    observation_id,
1507                    StateType.OBSERVATION,
1508                    trace_id,
1509                    self.task_manager,
1510                )
1511            else:
1512                return StatefulClient(
1513                    self.client, new_id, StateType.TRACE, new_id, self.task_manager
1514                )

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:
1516    def span(
1517        self,
1518        *,
1519        id: typing.Optional[str] = None,
1520        trace_id: typing.Optional[str] = None,
1521        parent_observation_id: typing.Optional[str] = None,
1522        name: typing.Optional[str] = None,
1523        start_time: typing.Optional[dt.datetime] = None,
1524        end_time: typing.Optional[dt.datetime] = None,
1525        metadata: typing.Optional[typing.Any] = None,
1526        level: typing.Optional[SpanLevel] = None,
1527        status_message: typing.Optional[str] = None,
1528        input: typing.Optional[typing.Any] = None,
1529        output: typing.Optional[typing.Any] = None,
1530        version: typing.Optional[str] = None,
1531        **kwargs,
1532    ) -> "StatefulSpanClient":
1533        """Create a span.
1534
1535        A span represents durations of units of work in a trace.
1536        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.
1537
1538        If no trace_id is provided, a new trace is created just for this span.
1539
1540        Args:
1541            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
1542            trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated.
1543            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1544            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
1545            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
1546            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
1547            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
1548            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.
1549            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.
1550            input (Optional[dict]): The input to the span. Can be any JSON object.
1551            output (Optional[dict]): The output to the span. Can be any JSON object.
1552            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1553            **kwargs: Additional keyword arguments to include in the span.
1554
1555        Returns:
1556            StatefulSpanClient: The created span.
1557
1558        Example:
1559            ```python
1560            from langfuse import Langfuse
1561
1562            langfuse = Langfuse()
1563
1564            trace = langfuse.trace(name = "llm-feature")
1565
1566            # Create a span
1567            retrieval = langfuse.span(name = "retrieval", trace_id = trace.id)
1568
1569            # Create a nested span
1570            nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id)
1571            ```
1572        """
1573        new_span_id = id or str(uuid.uuid4())
1574        new_trace_id = trace_id or str(uuid.uuid4())
1575        self.trace_id = new_trace_id
1576        try:
1577            span_body = {
1578                "id": new_span_id,
1579                "trace_id": new_trace_id,
1580                "name": name,
1581                "start_time": start_time or _get_timestamp(),
1582                "metadata": metadata,
1583                "input": input,
1584                "output": output,
1585                "level": level,
1586                "status_message": status_message,
1587                "parent_observation_id": parent_observation_id,
1588                "version": version,
1589                "end_time": end_time,
1590                "trace": {"release": self.release},
1591                **kwargs,
1592            }
1593
1594            if trace_id is None:
1595                self._generate_trace(new_trace_id, name or new_trace_id)
1596
1597            self.log.debug(f"Creating span {span_body}...")
1598
1599            span_body = CreateSpanBody(**span_body)
1600
1601            event = {
1602                "id": str(uuid.uuid4()),
1603                "type": "span-create",
1604                "body": span_body.dict(exclude_none=True),
1605            }
1606
1607            self.log.debug(f"Creating span {event}...")
1608            self.task_manager.add_task(event)
1609
1610        except Exception as e:
1611            self.log.exception(e)
1612        finally:
1613            self._log_memory_usage()
1614
1615            return StatefulSpanClient(
1616                self.client,
1617                new_span_id,
1618                StateType.OBSERVATION,
1619                new_trace_id,
1620                self.task_manager,
1621            )

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:
1623    def event(
1624        self,
1625        *,
1626        id: typing.Optional[str] = None,
1627        trace_id: typing.Optional[str] = None,
1628        parent_observation_id: typing.Optional[str] = None,
1629        name: typing.Optional[str] = None,
1630        start_time: typing.Optional[dt.datetime] = None,
1631        metadata: typing.Optional[typing.Any] = None,
1632        input: typing.Optional[typing.Any] = None,
1633        output: typing.Optional[typing.Any] = None,
1634        level: typing.Optional[SpanLevel] = None,
1635        status_message: typing.Optional[str] = None,
1636        version: typing.Optional[str] = None,
1637        **kwargs,
1638    ) -> "StatefulSpanClient":
1639        """Create an event.
1640
1641        An event represents a discrete event in a trace.
1642        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.
1643
1644        If no trace_id is provided, a new trace is created just for this event.
1645
1646        Args:
1647            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
1648            trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event.
1649            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1650            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
1651            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
1652            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
1653            input (Optional[Any]): The input to the event. Can be any JSON object.
1654            output (Optional[Any]): The output to the event. Can be any JSON object.
1655            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.
1656            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.
1657            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
1658            **kwargs: Additional keyword arguments to include in the event.
1659
1660        Returns:
1661            StatefulSpanClient: The created event.
1662
1663        Example:
1664            ```python
1665            from langfuse import Langfuse
1666
1667            langfuse = Langfuse()
1668
1669            trace = langfuse.trace(name = "llm-feature")
1670
1671            # Create an event
1672            retrieval = langfuse.event(name = "retrieval", trace_id = trace.id)
1673            ```
1674        """
1675        event_id = id or str(uuid.uuid4())
1676        new_trace_id = trace_id or str(uuid.uuid4())
1677        self.trace_id = new_trace_id
1678        try:
1679            event_body = {
1680                "id": event_id,
1681                "trace_id": new_trace_id,
1682                "name": name,
1683                "start_time": start_time or _get_timestamp(),
1684                "metadata": metadata,
1685                "input": input,
1686                "output": output,
1687                "level": level,
1688                "status_message": status_message,
1689                "parent_observation_id": parent_observation_id,
1690                "version": version,
1691                "trace": {"release": self.release},
1692                **kwargs,
1693            }
1694
1695            if trace_id is None:
1696                self._generate_trace(new_trace_id, name or new_trace_id)
1697
1698            request = CreateEventBody(**event_body)
1699
1700            event = {
1701                "id": str(uuid.uuid4()),
1702                "type": "event-create",
1703                "body": request.dict(exclude_none=True),
1704            }
1705
1706            self.log.debug(f"Creating event {event}...")
1707            self.task_manager.add_task(event)
1708
1709        except Exception as e:
1710            self.log.exception(e)
1711        finally:
1712            return StatefulSpanClient(
1713                self.client,
1714                event_id,
1715                StateType.OBSERVATION,
1716                new_trace_id,
1717                self.task_manager,
1718            )

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, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None, **kwargs) -> StatefulGenerationClient:
1720    def generation(
1721        self,
1722        *,
1723        id: typing.Optional[str] = None,
1724        trace_id: typing.Optional[str] = None,
1725        parent_observation_id: typing.Optional[str] = None,
1726        name: typing.Optional[str] = None,
1727        start_time: typing.Optional[dt.datetime] = None,
1728        end_time: typing.Optional[dt.datetime] = None,
1729        completion_start_time: typing.Optional[dt.datetime] = None,
1730        metadata: typing.Optional[typing.Any] = None,
1731        level: typing.Optional[SpanLevel] = None,
1732        status_message: typing.Optional[str] = None,
1733        version: typing.Optional[str] = None,
1734        model: typing.Optional[str] = None,
1735        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
1736        input: typing.Optional[typing.Any] = None,
1737        output: typing.Optional[typing.Any] = None,
1738        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
1739        prompt: typing.Optional[PromptClient] = None,
1740        **kwargs,
1741    ) -> "StatefulGenerationClient":
1742        """Create a generation.
1743
1744        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.
1745
1746        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.
1747
1748        If no trace_id is provided, a new trace is created just for this generation.
1749
1750        Args:
1751            id (Optional[str]): The id of the generation can be set, defaults to random id.
1752            trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created
1753            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1754            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
1755            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
1756            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
1757            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.
1758            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
1759            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.
1760            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.
1761            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1762            model (Optional[str]): The name of the model used for the generation.
1763            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
1764            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
1765            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
1766            usage (Optional[dict]): 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.
1767            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
1768            **kwargs: Additional keyword arguments to include in the generation.
1769
1770        Returns:
1771            StatefulGenerationClient: The created generation.
1772
1773        Example:
1774            ```python
1775            from langfuse import Langfuse
1776
1777            langfuse = Langfuse()
1778
1779            # Create a generation in Langfuse
1780            generation = langfuse.generation(
1781                name="summary-generation",
1782                model="gpt-3.5-turbo",
1783                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
1784                input=[{"role": "system", "content": "You are a helpful assistant."},
1785                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
1786                metadata={"interface": "whatsapp"}
1787            )
1788            ```
1789        """
1790        new_trace_id = trace_id or str(uuid.uuid4())
1791        new_generation_id = id or str(uuid.uuid4())
1792        self.trace_id = new_trace_id
1793        try:
1794            generation_body = {
1795                "id": new_generation_id,
1796                "trace_id": new_trace_id,
1797                "release": self.release,
1798                "name": name,
1799                "start_time": start_time or _get_timestamp(),
1800                "metadata": metadata,
1801                "input": input,
1802                "output": output,
1803                "level": level,
1804                "status_message": status_message,
1805                "parent_observation_id": parent_observation_id,
1806                "version": version,
1807                "end_time": end_time,
1808                "completion_start_time": completion_start_time,
1809                "model": model,
1810                "model_parameters": model_parameters,
1811                "usage": _convert_usage_input(usage) if usage is not None else None,
1812                "trace": {"release": self.release},
1813                **_create_prompt_context(prompt),
1814                **kwargs,
1815            }
1816
1817            if trace_id is None:
1818                trace = {
1819                    "id": new_trace_id,
1820                    "release": self.release,
1821                    "name": name,
1822                }
1823                request = TraceBody(**trace)
1824
1825                event = {
1826                    "id": str(uuid.uuid4()),
1827                    "type": "trace-create",
1828                    "body": request.dict(exclude_none=True),
1829                }
1830
1831                self.log.debug(f"Creating trace {event}...")
1832
1833                self.task_manager.add_task(event)
1834
1835            self.log.debug(f"Creating generation max {generation_body} {usage}...")
1836            request = CreateGenerationBody(**generation_body)
1837
1838            event = {
1839                "id": str(uuid.uuid4()),
1840                "type": "generation-create",
1841                "body": request.dict(exclude_none=True),
1842            }
1843
1844            self.log.debug(f"Creating top-level generation {event} ...")
1845            self.task_manager.add_task(event)
1846
1847        except Exception as e:
1848            self.log.exception(e)
1849        finally:
1850            return StatefulGenerationClient(
1851                self.client,
1852                new_generation_id,
1853                StateType.OBSERVATION,
1854                new_trace_id,
1855                self.task_manager,
1856            )

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]): 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.
  • 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):
1876    def join(self):
1877        """Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter.
1878
1879        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.
1880        To guarantee all messages have been delivered, you still need to call flush().
1881        """
1882        try:
1883            return self.task_manager.join()
1884        except Exception as e:
1885            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):
1887    def flush(self):
1888        """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.
1889
1890        Example:
1891            ```python
1892            from langfuse import Langfuse
1893
1894            langfuse = Langfuse()
1895
1896            # Some operations with Langfuse
1897
1898            # Flushing all events to end Langfuse cleanly
1899            langfuse.flush()
1900            ```
1901        """
1902        try:
1903            return self.task_manager.flush()
1904        except Exception as e:
1905            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):
1907    def shutdown(self):
1908        """Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated.
1909
1910        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.
1911        As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API.
1912        """
1913        try:
1914            return self.task_manager.shutdown()
1915        except Exception as e:
1916            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):
1919class StateType(Enum):
1920    """Enum to distinguish observation and trace states.
1921
1922    Attributes:
1923        OBSERVATION (int): Observation state.
1924        TRACE (int): Trace state.
1925    """
1926
1927    OBSERVATION = 1
1928    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:
1931class StatefulClient(object):
1932    """Base class for handling stateful operations in the Langfuse system.
1933
1934    This client is capable of creating different nested Langfuse objects like spans, generations, scores, and events,
1935    associating them with either an observation or a trace based on the specified state type.
1936
1937    Attributes:
1938        client (FernLangfuse): Core interface for Langfuse API interactions.
1939        id (str): Unique identifier of the stateful client (either observation or trace).
1940        state_type (StateType): Enum indicating whether the client is an observation or a trace.
1941        trace_id (str): Id of the trace associated with the stateful client.
1942        task_manager (TaskManager): Manager handling asynchronous tasks for the client.
1943    """
1944
1945    log = logging.getLogger("langfuse")
1946
1947    def __init__(
1948        self,
1949        client: FernLangfuse,
1950        id: str,
1951        state_type: StateType,
1952        trace_id: str,
1953        task_manager: TaskManager,
1954    ):
1955        """Initialize the StatefulClient.
1956
1957        Args:
1958            client (FernLangfuse): Core interface for Langfuse API interactions.
1959            id (str): Unique identifier of the stateful client (either observation or trace).
1960            state_type (StateType): Enum indicating whether the client is an observation or a trace.
1961            trace_id (str): Id of the trace associated with the stateful client.
1962            task_manager (TaskManager): Manager handling asynchronous tasks for the client.
1963        """
1964        self.client = client
1965        self.trace_id = trace_id
1966        self.id = id
1967        self.state_type = state_type
1968        self.task_manager = task_manager
1969
1970    def _add_state_to_event(self, body: dict):
1971        if self.state_type == StateType.OBSERVATION:
1972            body["parent_observation_id"] = self.id
1973            body["trace_id"] = self.trace_id
1974        else:
1975            body["trace_id"] = self.id
1976        return body
1977
1978    def _add_default_values(self, body: dict):
1979        if body.get("start_time") is None:
1980            body["start_time"] = _get_timestamp()
1981        return body
1982
1983    def generation(
1984        self,
1985        *,
1986        id: typing.Optional[str] = None,
1987        name: typing.Optional[str] = None,
1988        start_time: typing.Optional[dt.datetime] = None,
1989        end_time: typing.Optional[dt.datetime] = None,
1990        metadata: typing.Optional[typing.Any] = None,
1991        level: typing.Optional[SpanLevel] = None,
1992        status_message: typing.Optional[str] = None,
1993        version: typing.Optional[str] = None,
1994        completion_start_time: typing.Optional[dt.datetime] = None,
1995        model: typing.Optional[str] = None,
1996        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
1997        input: typing.Optional[typing.Any] = None,
1998        output: typing.Optional[typing.Any] = None,
1999        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2000        prompt: typing.Optional[PromptClient] = None,
2001        **kwargs,
2002    ) -> "StatefulGenerationClient":
2003        """Create a generation nested within the current observation or trace.
2004
2005        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.
2006
2007        Args:
2008            id (Optional[str]): The id of the generation can be set, defaults to random id.
2009            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2010            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2011            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2012            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.
2013            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2014            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.
2015            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.
2016            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2017            model (Optional[str]): The name of the model used for the generation.
2018            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2019            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2020            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2021            usage (Optional[dict]): 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.
2022            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2023            **kwargs: Additional keyword arguments to include in the generation.
2024
2025        Returns:
2026            StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.
2027
2028        Example:
2029            ```python
2030            from langfuse import Langfuse
2031
2032            langfuse = Langfuse()
2033
2034            # Create a trace
2035            trace = langfuse.trace(name = "llm-feature")
2036
2037            # Create a nested generation in Langfuse
2038            generation = trace.generation(
2039                name="summary-generation",
2040                model="gpt-3.5-turbo",
2041                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
2042                input=[{"role": "system", "content": "You are a helpful assistant."},
2043                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
2044                metadata={"interface": "whatsapp"}
2045            )
2046            ```
2047        """
2048        generation_id = id or str(uuid.uuid4())
2049        try:
2050            generation_body = {
2051                "id": generation_id,
2052                "name": name,
2053                "start_time": start_time or _get_timestamp(),
2054                "metadata": metadata,
2055                "level": level,
2056                "status_message": status_message,
2057                "version": version,
2058                "end_time": end_time,
2059                "completion_start_time": completion_start_time,
2060                "model": model,
2061                "model_parameters": model_parameters,
2062                "input": input,
2063                "output": output,
2064                "usage": _convert_usage_input(usage) if usage is not None else None,
2065                **_create_prompt_context(prompt),
2066                **kwargs,
2067            }
2068
2069            generation_body = self._add_state_to_event(generation_body)
2070            new_body = self._add_default_values(generation_body)
2071
2072            new_body = CreateGenerationBody(**new_body)
2073
2074            event = {
2075                "id": str(uuid.uuid4()),
2076                "type": "generation-create",
2077                "body": new_body.dict(exclude_none=True, exclude_unset=False),
2078            }
2079
2080            self.log.debug(f"Creating generation {new_body}...")
2081            self.task_manager.add_task(event)
2082
2083        except Exception as e:
2084            self.log.exception(e)
2085        finally:
2086            return StatefulGenerationClient(
2087                self.client,
2088                generation_id,
2089                StateType.OBSERVATION,
2090                self.trace_id,
2091                task_manager=self.task_manager,
2092            )
2093
2094    def span(
2095        self,
2096        *,
2097        id: typing.Optional[str] = None,
2098        name: typing.Optional[str] = None,
2099        start_time: typing.Optional[dt.datetime] = None,
2100        end_time: typing.Optional[dt.datetime] = None,
2101        metadata: typing.Optional[typing.Any] = None,
2102        input: typing.Optional[typing.Any] = None,
2103        output: typing.Optional[typing.Any] = None,
2104        level: typing.Optional[SpanLevel] = None,
2105        status_message: typing.Optional[str] = None,
2106        version: typing.Optional[str] = None,
2107        **kwargs,
2108    ) -> "StatefulSpanClient":
2109        """Create a span nested within the current observation or trace.
2110
2111        A span represents durations of units of work in a trace.
2112
2113        Args:
2114            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
2115            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2116            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2117            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2118            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2119            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.
2120            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.
2121            input (Optional[dict]): The input to the span. Can be any JSON object.
2122            output (Optional[dict]): The output to the span. Can be any JSON object.
2123            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2124            **kwargs: Additional keyword arguments to include in the span.
2125
2126        Returns:
2127            StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.
2128
2129        Example:
2130            ```python
2131            from langfuse import Langfuse
2132
2133            langfuse = Langfuse()
2134
2135            # Create a trace
2136            trace = langfuse.trace(name = "llm-feature")
2137
2138            # Create a span
2139            retrieval = langfuse.span(name = "retrieval")
2140            ```
2141        """
2142        span_id = id or str(uuid.uuid4())
2143        try:
2144            span_body = {
2145                "id": span_id,
2146                "name": name,
2147                "start_time": start_time or _get_timestamp(),
2148                "metadata": metadata,
2149                "input": input,
2150                "output": output,
2151                "level": level,
2152                "status_message": status_message,
2153                "version": version,
2154                "end_time": end_time,
2155                **kwargs,
2156            }
2157
2158            self.log.debug(f"Creating span {span_body}...")
2159
2160            new_dict = self._add_state_to_event(span_body)
2161            new_body = self._add_default_values(new_dict)
2162
2163            event = CreateSpanBody(**new_body)
2164
2165            event = {
2166                "id": str(uuid.uuid4()),
2167                "type": "span-create",
2168                "body": event.dict(exclude_none=True),
2169            }
2170
2171            self.task_manager.add_task(event)
2172        except Exception as e:
2173            self.log.exception(e)
2174        finally:
2175            return StatefulSpanClient(
2176                self.client,
2177                span_id,
2178                StateType.OBSERVATION,
2179                self.trace_id,
2180                task_manager=self.task_manager,
2181            )
2182
2183    @overload
2184    def score(
2185        self,
2186        *,
2187        id: typing.Optional[str] = None,
2188        name: str,
2189        value: float,
2190        data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None,
2191        comment: typing.Optional[str] = None,
2192        config_id: typing.Optional[str] = None,
2193        **kwargs,
2194    ) -> "StatefulClient": ...
2195
2196    @overload
2197    def score(
2198        self,
2199        *,
2200        id: typing.Optional[str] = None,
2201        name: str,
2202        value: str,
2203        data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL",
2204        comment: typing.Optional[str] = None,
2205        config_id: typing.Optional[str] = None,
2206        **kwargs,
2207    ) -> "StatefulClient": ...
2208
2209    def score(
2210        self,
2211        *,
2212        id: typing.Optional[str] = None,
2213        name: str,
2214        value: typing.Union[float, str],
2215        data_type: typing.Optional[ScoreDataType] = None,
2216        comment: typing.Optional[str] = None,
2217        config_id: typing.Optional[str] = None,
2218        **kwargs,
2219    ) -> "StatefulClient":
2220        """Create a score attached for the current observation or trace.
2221
2222        Args:
2223            name (str): Identifier of the score.
2224            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.
2225            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.
2226              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.
2227            comment (Optional[str]): Additional context/explanation of the score.
2228            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
2229            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
2230            **kwargs: Additional keyword arguments to include in the score.
2231
2232        Returns:
2233            StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.
2234
2235        Example:
2236            ```python
2237            from langfuse import Langfuse
2238
2239            langfuse = Langfuse()
2240
2241            # Create a trace
2242            trace = langfuse.trace(name="example-application")
2243
2244            # Add score to the trace
2245            trace = trace.score(
2246                name="user-explicit-feedback",
2247                value=0.8,
2248                comment="I like how personalized the response is"
2249            )
2250            ```
2251        """
2252        score_id = id or str(uuid.uuid4())
2253        try:
2254            new_score = {
2255                "id": score_id,
2256                "trace_id": self.trace_id,
2257                "name": name,
2258                "value": value,
2259                "data_type": data_type,
2260                "comment": comment,
2261                "config_id": config_id,
2262                **kwargs,
2263            }
2264
2265            self.log.debug(f"Creating score {new_score}...")
2266
2267            new_dict = self._add_state_to_event(new_score)
2268
2269            if self.state_type == StateType.OBSERVATION:
2270                new_dict["observationId"] = self.id
2271
2272            request = ScoreBody(**new_dict)
2273
2274            event = {
2275                "id": str(uuid.uuid4()),
2276                "type": "score-create",
2277                "body": request.dict(exclude_none=True),
2278            }
2279
2280            self.task_manager.add_task(event)
2281
2282        except Exception as e:
2283            self.log.exception(e)
2284        finally:
2285            return StatefulClient(
2286                self.client,
2287                self.id,
2288                self.state_type,
2289                self.trace_id,
2290                task_manager=self.task_manager,
2291            )
2292
2293    def event(
2294        self,
2295        *,
2296        id: typing.Optional[str] = None,
2297        name: typing.Optional[str] = None,
2298        start_time: typing.Optional[dt.datetime] = None,
2299        metadata: typing.Optional[typing.Any] = None,
2300        input: typing.Optional[typing.Any] = None,
2301        output: typing.Optional[typing.Any] = None,
2302        level: typing.Optional[SpanLevel] = None,
2303        status_message: typing.Optional[str] = None,
2304        version: typing.Optional[str] = None,
2305        **kwargs,
2306    ) -> "StatefulClient":
2307        """Create an event nested within the current observation or trace.
2308
2309        An event represents a discrete event in a trace.
2310
2311        Args:
2312            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
2313            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
2314            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
2315            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
2316            input (Optional[Any]): The input to the event. Can be any JSON object.
2317            output (Optional[Any]): The output to the event. Can be any JSON object.
2318            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.
2319            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.
2320            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
2321            **kwargs: Additional keyword arguments to include in the event.
2322
2323        Returns:
2324            StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations.
2325
2326        Example:
2327            ```python
2328            from langfuse import Langfuse
2329
2330            langfuse = Langfuse()
2331
2332            # Create a trace
2333            trace = langfuse.trace(name = "llm-feature")
2334
2335            # Create an event
2336            retrieval = trace.event(name = "retrieval")
2337            ```
2338        """
2339        event_id = id or str(uuid.uuid4())
2340        try:
2341            event_body = {
2342                "id": event_id,
2343                "name": name,
2344                "start_time": start_time or _get_timestamp(),
2345                "metadata": metadata,
2346                "input": input,
2347                "output": output,
2348                "level": level,
2349                "status_message": status_message,
2350                "version": version,
2351                **kwargs,
2352            }
2353
2354            new_dict = self._add_state_to_event(event_body)
2355            new_body = self._add_default_values(new_dict)
2356
2357            request = CreateEventBody(**new_body)
2358
2359            event = {
2360                "id": str(uuid.uuid4()),
2361                "type": "event-create",
2362                "body": request.dict(exclude_none=True),
2363            }
2364
2365            self.log.debug(f"Creating event {event}...")
2366            self.task_manager.add_task(event)
2367
2368        except Exception as e:
2369            self.log.exception(e)
2370        finally:
2371            return StatefulClient(
2372                self.client,
2373                event_id,
2374                StateType.OBSERVATION,
2375                self.trace_id,
2376                self.task_manager,
2377            )
2378
2379    def get_trace_url(self):
2380        """Get the URL to see the current trace in the Langfuse UI."""
2381        return f"{self.client._client_wrapper._base_url}/trace/{self.trace_id}"

Base class for handling stateful operations in the Langfuse system.

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

Attributes:
  • client (FernLangfuse): Core interface for Langfuse API interactions.
  • id (str): Unique identifier of the stateful client (either observation or trace).
  • state_type (StateType): Enum indicating whether the client is an observation or a trace.
  • trace_id (str): Id of the trace associated with the stateful client.
  • task_manager (TaskManager): Manager handling asynchronous tasks for the client.
StatefulClient( client: langfuse.api.client.FernLangfuse, id: str, state_type: StateType, trace_id: str, task_manager: langfuse.task_manager.TaskManager)
1947    def __init__(
1948        self,
1949        client: FernLangfuse,
1950        id: str,
1951        state_type: StateType,
1952        trace_id: str,
1953        task_manager: TaskManager,
1954    ):
1955        """Initialize the StatefulClient.
1956
1957        Args:
1958            client (FernLangfuse): Core interface for Langfuse API interactions.
1959            id (str): Unique identifier of the stateful client (either observation or trace).
1960            state_type (StateType): Enum indicating whether the client is an observation or a trace.
1961            trace_id (str): Id of the trace associated with the stateful client.
1962            task_manager (TaskManager): Manager handling asynchronous tasks for the client.
1963        """
1964        self.client = client
1965        self.trace_id = trace_id
1966        self.id = id
1967        self.state_type = state_type
1968        self.task_manager = task_manager

Initialize the StatefulClient.

Arguments:
  • client (FernLangfuse): Core interface for Langfuse API interactions.
  • id (str): Unique identifier of the stateful client (either observation or trace).
  • state_type (StateType): Enum indicating whether the client is an observation or a trace.
  • trace_id (str): Id of the trace associated with the stateful client.
  • task_manager (TaskManager): Manager handling asynchronous tasks for the client.
log = <Logger langfuse (WARNING)>
client
trace_id
id
state_type
task_manager
def generation( self, *, id: Optional[str] = None, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, input: Optional[Any] = None, output: Optional[Any] = None, usage: Union[pydantic.v1.main.BaseModel, langfuse.model.ModelUsage, NoneType] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None, **kwargs) -> StatefulGenerationClient:
1983    def generation(
1984        self,
1985        *,
1986        id: typing.Optional[str] = None,
1987        name: typing.Optional[str] = None,
1988        start_time: typing.Optional[dt.datetime] = None,
1989        end_time: typing.Optional[dt.datetime] = None,
1990        metadata: typing.Optional[typing.Any] = None,
1991        level: typing.Optional[SpanLevel] = None,
1992        status_message: typing.Optional[str] = None,
1993        version: typing.Optional[str] = None,
1994        completion_start_time: typing.Optional[dt.datetime] = None,
1995        model: typing.Optional[str] = None,
1996        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
1997        input: typing.Optional[typing.Any] = None,
1998        output: typing.Optional[typing.Any] = None,
1999        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2000        prompt: typing.Optional[PromptClient] = None,
2001        **kwargs,
2002    ) -> "StatefulGenerationClient":
2003        """Create a generation nested within the current observation or trace.
2004
2005        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.
2006
2007        Args:
2008            id (Optional[str]): The id of the generation can be set, defaults to random id.
2009            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2010            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2011            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2012            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.
2013            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2014            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.
2015            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.
2016            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2017            model (Optional[str]): The name of the model used for the generation.
2018            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2019            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2020            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2021            usage (Optional[dict]): 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.
2022            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2023            **kwargs: Additional keyword arguments to include in the generation.
2024
2025        Returns:
2026            StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.
2027
2028        Example:
2029            ```python
2030            from langfuse import Langfuse
2031
2032            langfuse = Langfuse()
2033
2034            # Create a trace
2035            trace = langfuse.trace(name = "llm-feature")
2036
2037            # Create a nested generation in Langfuse
2038            generation = trace.generation(
2039                name="summary-generation",
2040                model="gpt-3.5-turbo",
2041                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
2042                input=[{"role": "system", "content": "You are a helpful assistant."},
2043                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
2044                metadata={"interface": "whatsapp"}
2045            )
2046            ```
2047        """
2048        generation_id = id or str(uuid.uuid4())
2049        try:
2050            generation_body = {
2051                "id": generation_id,
2052                "name": name,
2053                "start_time": start_time or _get_timestamp(),
2054                "metadata": metadata,
2055                "level": level,
2056                "status_message": status_message,
2057                "version": version,
2058                "end_time": end_time,
2059                "completion_start_time": completion_start_time,
2060                "model": model,
2061                "model_parameters": model_parameters,
2062                "input": input,
2063                "output": output,
2064                "usage": _convert_usage_input(usage) if usage is not None else None,
2065                **_create_prompt_context(prompt),
2066                **kwargs,
2067            }
2068
2069            generation_body = self._add_state_to_event(generation_body)
2070            new_body = self._add_default_values(generation_body)
2071
2072            new_body = CreateGenerationBody(**new_body)
2073
2074            event = {
2075                "id": str(uuid.uuid4()),
2076                "type": "generation-create",
2077                "body": new_body.dict(exclude_none=True, exclude_unset=False),
2078            }
2079
2080            self.log.debug(f"Creating generation {new_body}...")
2081            self.task_manager.add_task(event)
2082
2083        except Exception as e:
2084            self.log.exception(e)
2085        finally:
2086            return StatefulGenerationClient(
2087                self.client,
2088                generation_id,
2089                StateType.OBSERVATION,
2090                self.trace_id,
2091                task_manager=self.task_manager,
2092            )

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]): 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.
  • 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:
2094    def span(
2095        self,
2096        *,
2097        id: typing.Optional[str] = None,
2098        name: typing.Optional[str] = None,
2099        start_time: typing.Optional[dt.datetime] = None,
2100        end_time: typing.Optional[dt.datetime] = None,
2101        metadata: typing.Optional[typing.Any] = None,
2102        input: typing.Optional[typing.Any] = None,
2103        output: typing.Optional[typing.Any] = None,
2104        level: typing.Optional[SpanLevel] = None,
2105        status_message: typing.Optional[str] = None,
2106        version: typing.Optional[str] = None,
2107        **kwargs,
2108    ) -> "StatefulSpanClient":
2109        """Create a span nested within the current observation or trace.
2110
2111        A span represents durations of units of work in a trace.
2112
2113        Args:
2114            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
2115            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2116            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2117            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2118            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2119            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.
2120            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.
2121            input (Optional[dict]): The input to the span. Can be any JSON object.
2122            output (Optional[dict]): The output to the span. Can be any JSON object.
2123            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2124            **kwargs: Additional keyword arguments to include in the span.
2125
2126        Returns:
2127            StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.
2128
2129        Example:
2130            ```python
2131            from langfuse import Langfuse
2132
2133            langfuse = Langfuse()
2134
2135            # Create a trace
2136            trace = langfuse.trace(name = "llm-feature")
2137
2138            # Create a span
2139            retrieval = langfuse.span(name = "retrieval")
2140            ```
2141        """
2142        span_id = id or str(uuid.uuid4())
2143        try:
2144            span_body = {
2145                "id": span_id,
2146                "name": name,
2147                "start_time": start_time or _get_timestamp(),
2148                "metadata": metadata,
2149                "input": input,
2150                "output": output,
2151                "level": level,
2152                "status_message": status_message,
2153                "version": version,
2154                "end_time": end_time,
2155                **kwargs,
2156            }
2157
2158            self.log.debug(f"Creating span {span_body}...")
2159
2160            new_dict = self._add_state_to_event(span_body)
2161            new_body = self._add_default_values(new_dict)
2162
2163            event = CreateSpanBody(**new_body)
2164
2165            event = {
2166                "id": str(uuid.uuid4()),
2167                "type": "span-create",
2168                "body": event.dict(exclude_none=True),
2169            }
2170
2171            self.task_manager.add_task(event)
2172        except Exception as e:
2173            self.log.exception(e)
2174        finally:
2175            return StatefulSpanClient(
2176                self.client,
2177                span_id,
2178                StateType.OBSERVATION,
2179                self.trace_id,
2180                task_manager=self.task_manager,
2181            )

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:
2209    def score(
2210        self,
2211        *,
2212        id: typing.Optional[str] = None,
2213        name: str,
2214        value: typing.Union[float, str],
2215        data_type: typing.Optional[ScoreDataType] = None,
2216        comment: typing.Optional[str] = None,
2217        config_id: typing.Optional[str] = None,
2218        **kwargs,
2219    ) -> "StatefulClient":
2220        """Create a score attached for the current observation or trace.
2221
2222        Args:
2223            name (str): Identifier of the score.
2224            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.
2225            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.
2226              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.
2227            comment (Optional[str]): Additional context/explanation of the score.
2228            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
2229            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
2230            **kwargs: Additional keyword arguments to include in the score.
2231
2232        Returns:
2233            StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.
2234
2235        Example:
2236            ```python
2237            from langfuse import Langfuse
2238
2239            langfuse = Langfuse()
2240
2241            # Create a trace
2242            trace = langfuse.trace(name="example-application")
2243
2244            # Add score to the trace
2245            trace = trace.score(
2246                name="user-explicit-feedback",
2247                value=0.8,
2248                comment="I like how personalized the response is"
2249            )
2250            ```
2251        """
2252        score_id = id or str(uuid.uuid4())
2253        try:
2254            new_score = {
2255                "id": score_id,
2256                "trace_id": self.trace_id,
2257                "name": name,
2258                "value": value,
2259                "data_type": data_type,
2260                "comment": comment,
2261                "config_id": config_id,
2262                **kwargs,
2263            }
2264
2265            self.log.debug(f"Creating score {new_score}...")
2266
2267            new_dict = self._add_state_to_event(new_score)
2268
2269            if self.state_type == StateType.OBSERVATION:
2270                new_dict["observationId"] = self.id
2271
2272            request = ScoreBody(**new_dict)
2273
2274            event = {
2275                "id": str(uuid.uuid4()),
2276                "type": "score-create",
2277                "body": request.dict(exclude_none=True),
2278            }
2279
2280            self.task_manager.add_task(event)
2281
2282        except Exception as e:
2283            self.log.exception(e)
2284        finally:
2285            return StatefulClient(
2286                self.client,
2287                self.id,
2288                self.state_type,
2289                self.trace_id,
2290                task_manager=self.task_manager,
2291            )

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:
2293    def event(
2294        self,
2295        *,
2296        id: typing.Optional[str] = None,
2297        name: typing.Optional[str] = None,
2298        start_time: typing.Optional[dt.datetime] = None,
2299        metadata: typing.Optional[typing.Any] = None,
2300        input: typing.Optional[typing.Any] = None,
2301        output: typing.Optional[typing.Any] = None,
2302        level: typing.Optional[SpanLevel] = None,
2303        status_message: typing.Optional[str] = None,
2304        version: typing.Optional[str] = None,
2305        **kwargs,
2306    ) -> "StatefulClient":
2307        """Create an event nested within the current observation or trace.
2308
2309        An event represents a discrete event in a trace.
2310
2311        Args:
2312            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
2313            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
2314            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
2315            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
2316            input (Optional[Any]): The input to the event. Can be any JSON object.
2317            output (Optional[Any]): The output to the event. Can be any JSON object.
2318            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.
2319            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.
2320            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
2321            **kwargs: Additional keyword arguments to include in the event.
2322
2323        Returns:
2324            StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations.
2325
2326        Example:
2327            ```python
2328            from langfuse import Langfuse
2329
2330            langfuse = Langfuse()
2331
2332            # Create a trace
2333            trace = langfuse.trace(name = "llm-feature")
2334
2335            # Create an event
2336            retrieval = trace.event(name = "retrieval")
2337            ```
2338        """
2339        event_id = id or str(uuid.uuid4())
2340        try:
2341            event_body = {
2342                "id": event_id,
2343                "name": name,
2344                "start_time": start_time or _get_timestamp(),
2345                "metadata": metadata,
2346                "input": input,
2347                "output": output,
2348                "level": level,
2349                "status_message": status_message,
2350                "version": version,
2351                **kwargs,
2352            }
2353
2354            new_dict = self._add_state_to_event(event_body)
2355            new_body = self._add_default_values(new_dict)
2356
2357            request = CreateEventBody(**new_body)
2358
2359            event = {
2360                "id": str(uuid.uuid4()),
2361                "type": "event-create",
2362                "body": request.dict(exclude_none=True),
2363            }
2364
2365            self.log.debug(f"Creating event {event}...")
2366            self.task_manager.add_task(event)
2367
2368        except Exception as e:
2369            self.log.exception(e)
2370        finally:
2371            return StatefulClient(
2372                self.client,
2373                event_id,
2374                StateType.OBSERVATION,
2375                self.trace_id,
2376                self.task_manager,
2377            )

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):
2379    def get_trace_url(self):
2380        """Get the URL to see the current trace in the Langfuse UI."""
2381        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):
2384class StatefulGenerationClient(StatefulClient):
2385    """Class for handling stateful operations of generations in the Langfuse system. Inherits from StatefulClient.
2386
2387    This client extends the capabilities of the StatefulClient to specifically handle generation,
2388    allowing for the creation, update, and termination of generation processes in Langfuse.
2389
2390    Attributes:
2391        client (FernLangfuse): Core interface for Langfuse API interaction.
2392        id (str): Unique identifier of the generation.
2393        state_type (StateType): Type of the stateful entity (observation or trace).
2394        trace_id (str): Id of trace associated with the generation.
2395        task_manager (TaskManager): Manager for handling asynchronous tasks.
2396    """
2397
2398    log = logging.getLogger("langfuse")
2399
2400    def __init__(
2401        self,
2402        client: FernLangfuse,
2403        id: str,
2404        state_type: StateType,
2405        trace_id: str,
2406        task_manager: TaskManager,
2407    ):
2408        """Initialize the StatefulGenerationClient."""
2409        super().__init__(client, id, state_type, trace_id, task_manager)
2410
2411    # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY
2412    def update(
2413        self,
2414        *,
2415        name: typing.Optional[str] = None,
2416        start_time: typing.Optional[dt.datetime] = None,
2417        end_time: typing.Optional[dt.datetime] = None,
2418        completion_start_time: typing.Optional[dt.datetime] = None,
2419        metadata: typing.Optional[typing.Any] = None,
2420        level: typing.Optional[SpanLevel] = None,
2421        status_message: typing.Optional[str] = None,
2422        version: typing.Optional[str] = None,
2423        model: typing.Optional[str] = None,
2424        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2425        input: typing.Optional[typing.Any] = None,
2426        output: typing.Optional[typing.Any] = None,
2427        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2428        prompt: typing.Optional[PromptClient] = None,
2429        **kwargs,
2430    ) -> "StatefulGenerationClient":
2431        """Update the generation.
2432
2433        Args:
2434            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2435            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2436            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2437            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.
2438            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2439            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.
2440            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.
2441            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2442            model (Optional[str]): The name of the model used for the generation.
2443            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2444            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2445            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2446            usage (Optional[dict]): 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.
2447            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2448            **kwargs: Additional keyword arguments to include in the generation.
2449
2450        Returns:
2451            StatefulGenerationClient: The updated generation. Passthrough for chaining.
2452
2453        Example:
2454            ```python
2455            from langfuse import Langfuse
2456
2457            langfuse = Langfuse()
2458
2459            # Create a trace
2460            trace = langfuse.trace(name = "llm-feature")
2461
2462            # Create a nested generation in Langfuse
2463            generation = trace.generation(name="summary-generation")
2464
2465            # Update the generation
2466            generation = generation.update(metadata={"interface": "whatsapp"})
2467            ```
2468        """
2469        try:
2470            generation_body = {
2471                "id": self.id,
2472                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2473                "name": name,
2474                "start_time": start_time,
2475                "metadata": metadata,
2476                "level": level,
2477                "status_message": status_message,
2478                "version": version,
2479                "end_time": end_time,
2480                "completion_start_time": completion_start_time,
2481                "model": model,
2482                "model_parameters": model_parameters,
2483                "input": input,
2484                "output": output,
2485                "usage": _convert_usage_input(usage) if usage is not None else None,
2486                **_create_prompt_context(prompt),
2487                **kwargs,
2488            }
2489
2490            self.log.debug(f"Update generation {generation_body}...")
2491
2492            request = UpdateGenerationBody(**generation_body)
2493
2494            event = {
2495                "id": str(uuid.uuid4()),
2496                "type": "generation-update",
2497                "body": request.dict(exclude_none=True, exclude_unset=False),
2498            }
2499
2500            self.log.debug(f"Update generation {event}...")
2501            self.task_manager.add_task(event)
2502
2503        except Exception as e:
2504            self.log.exception(e)
2505        finally:
2506            return StatefulGenerationClient(
2507                self.client,
2508                self.id,
2509                StateType.OBSERVATION,
2510                self.trace_id,
2511                task_manager=self.task_manager,
2512            )
2513
2514    def end(
2515        self,
2516        *,
2517        name: typing.Optional[str] = None,
2518        start_time: typing.Optional[dt.datetime] = None,
2519        end_time: typing.Optional[dt.datetime] = None,
2520        completion_start_time: typing.Optional[dt.datetime] = None,
2521        metadata: typing.Optional[typing.Any] = None,
2522        level: typing.Optional[SpanLevel] = None,
2523        status_message: typing.Optional[str] = None,
2524        version: typing.Optional[str] = None,
2525        model: typing.Optional[str] = None,
2526        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2527        input: typing.Optional[typing.Any] = None,
2528        output: typing.Optional[typing.Any] = None,
2529        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2530        prompt: typing.Optional[PromptClient] = None,
2531        **kwargs,
2532    ) -> "StatefulGenerationClient":
2533        """End the generation, optionally updating its properties.
2534
2535        Args:
2536            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2537            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2538            end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
2539            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.
2540            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2541            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.
2542            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.
2543            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2544            model (Optional[str]): The name of the model used for the generation.
2545            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2546            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2547            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2548            usage (Optional[dict]): 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.
2549            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2550            **kwargs: Additional keyword arguments to include in the generation.
2551
2552        Returns:
2553            StatefulGenerationClient: The ended generation. Passthrough for chaining.
2554
2555        Example:
2556            ```python
2557            from langfuse import Langfuse
2558
2559            langfuse = Langfuse()
2560
2561            # Create a trace
2562            trace = langfuse.trace(name = "llm-feature")
2563
2564            # Create a nested generation in Langfuse
2565            generation = trace.generation(name="summary-generation")
2566
2567            # End the generation and update its properties
2568            generation = generation.end(metadata={"interface": "whatsapp"})
2569            ```
2570        """
2571        return self.update(
2572            name=name,
2573            start_time=start_time,
2574            end_time=end_time or _get_timestamp(),
2575            metadata=metadata,
2576            level=level,
2577            status_message=status_message,
2578            version=version,
2579            completion_start_time=completion_start_time,
2580            model=model,
2581            model_parameters=model_parameters,
2582            input=input,
2583            output=output,
2584            usage=usage,
2585            prompt=prompt,
2586            **kwargs,
2587        )

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.TaskManager)
2400    def __init__(
2401        self,
2402        client: FernLangfuse,
2403        id: str,
2404        state_type: StateType,
2405        trace_id: str,
2406        task_manager: TaskManager,
2407    ):
2408        """Initialize the StatefulGenerationClient."""
2409        super().__init__(client, id, state_type, trace_id, task_manager)

Initialize the StatefulGenerationClient.

log = <Logger langfuse (WARNING)>
def update( self, *, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, completion_start_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, input: Optional[Any] = None, output: Optional[Any] = None, usage: Union[pydantic.v1.main.BaseModel, langfuse.model.ModelUsage, NoneType] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None, **kwargs) -> StatefulGenerationClient:
2412    def update(
2413        self,
2414        *,
2415        name: typing.Optional[str] = None,
2416        start_time: typing.Optional[dt.datetime] = None,
2417        end_time: typing.Optional[dt.datetime] = None,
2418        completion_start_time: typing.Optional[dt.datetime] = None,
2419        metadata: typing.Optional[typing.Any] = None,
2420        level: typing.Optional[SpanLevel] = None,
2421        status_message: typing.Optional[str] = None,
2422        version: typing.Optional[str] = None,
2423        model: typing.Optional[str] = None,
2424        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2425        input: typing.Optional[typing.Any] = None,
2426        output: typing.Optional[typing.Any] = None,
2427        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2428        prompt: typing.Optional[PromptClient] = None,
2429        **kwargs,
2430    ) -> "StatefulGenerationClient":
2431        """Update the generation.
2432
2433        Args:
2434            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2435            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2436            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2437            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.
2438            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2439            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.
2440            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.
2441            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2442            model (Optional[str]): The name of the model used for the generation.
2443            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2444            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2445            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2446            usage (Optional[dict]): 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.
2447            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2448            **kwargs: Additional keyword arguments to include in the generation.
2449
2450        Returns:
2451            StatefulGenerationClient: The updated generation. Passthrough for chaining.
2452
2453        Example:
2454            ```python
2455            from langfuse import Langfuse
2456
2457            langfuse = Langfuse()
2458
2459            # Create a trace
2460            trace = langfuse.trace(name = "llm-feature")
2461
2462            # Create a nested generation in Langfuse
2463            generation = trace.generation(name="summary-generation")
2464
2465            # Update the generation
2466            generation = generation.update(metadata={"interface": "whatsapp"})
2467            ```
2468        """
2469        try:
2470            generation_body = {
2471                "id": self.id,
2472                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2473                "name": name,
2474                "start_time": start_time,
2475                "metadata": metadata,
2476                "level": level,
2477                "status_message": status_message,
2478                "version": version,
2479                "end_time": end_time,
2480                "completion_start_time": completion_start_time,
2481                "model": model,
2482                "model_parameters": model_parameters,
2483                "input": input,
2484                "output": output,
2485                "usage": _convert_usage_input(usage) if usage is not None else None,
2486                **_create_prompt_context(prompt),
2487                **kwargs,
2488            }
2489
2490            self.log.debug(f"Update generation {generation_body}...")
2491
2492            request = UpdateGenerationBody(**generation_body)
2493
2494            event = {
2495                "id": str(uuid.uuid4()),
2496                "type": "generation-update",
2497                "body": request.dict(exclude_none=True, exclude_unset=False),
2498            }
2499
2500            self.log.debug(f"Update generation {event}...")
2501            self.task_manager.add_task(event)
2502
2503        except Exception as e:
2504            self.log.exception(e)
2505        finally:
2506            return StatefulGenerationClient(
2507                self.client,
2508                self.id,
2509                StateType.OBSERVATION,
2510                self.trace_id,
2511                task_manager=self.task_manager,
2512            )

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]): 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.
  • 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, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None, **kwargs) -> StatefulGenerationClient:
2514    def end(
2515        self,
2516        *,
2517        name: typing.Optional[str] = None,
2518        start_time: typing.Optional[dt.datetime] = None,
2519        end_time: typing.Optional[dt.datetime] = None,
2520        completion_start_time: typing.Optional[dt.datetime] = None,
2521        metadata: typing.Optional[typing.Any] = None,
2522        level: typing.Optional[SpanLevel] = None,
2523        status_message: typing.Optional[str] = None,
2524        version: typing.Optional[str] = None,
2525        model: typing.Optional[str] = None,
2526        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2527        input: typing.Optional[typing.Any] = None,
2528        output: typing.Optional[typing.Any] = None,
2529        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2530        prompt: typing.Optional[PromptClient] = None,
2531        **kwargs,
2532    ) -> "StatefulGenerationClient":
2533        """End the generation, optionally updating its properties.
2534
2535        Args:
2536            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2537            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2538            end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
2539            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.
2540            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2541            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.
2542            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.
2543            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2544            model (Optional[str]): The name of the model used for the generation.
2545            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2546            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2547            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2548            usage (Optional[dict]): 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.
2549            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2550            **kwargs: Additional keyword arguments to include in the generation.
2551
2552        Returns:
2553            StatefulGenerationClient: The ended generation. Passthrough for chaining.
2554
2555        Example:
2556            ```python
2557            from langfuse import Langfuse
2558
2559            langfuse = Langfuse()
2560
2561            # Create a trace
2562            trace = langfuse.trace(name = "llm-feature")
2563
2564            # Create a nested generation in Langfuse
2565            generation = trace.generation(name="summary-generation")
2566
2567            # End the generation and update its properties
2568            generation = generation.end(metadata={"interface": "whatsapp"})
2569            ```
2570        """
2571        return self.update(
2572            name=name,
2573            start_time=start_time,
2574            end_time=end_time or _get_timestamp(),
2575            metadata=metadata,
2576            level=level,
2577            status_message=status_message,
2578            version=version,
2579            completion_start_time=completion_start_time,
2580            model=model,
2581            model_parameters=model_parameters,
2582            input=input,
2583            output=output,
2584            usage=usage,
2585            prompt=prompt,
2586            **kwargs,
2587        )

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]): 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.
  • 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):
2590class StatefulSpanClient(StatefulClient):
2591    """Class for handling stateful operations of spans in the Langfuse system. Inherits from StatefulClient.
2592
2593    Attributes:
2594        client (FernLangfuse): Core interface for Langfuse API interaction.
2595        id (str): Unique identifier of the span.
2596        state_type (StateType): Type of the stateful entity (observation or trace).
2597        trace_id (str): Id of trace associated with the span.
2598        task_manager (TaskManager): Manager for handling asynchronous tasks.
2599    """
2600
2601    log = logging.getLogger("langfuse")
2602
2603    def __init__(
2604        self,
2605        client: FernLangfuse,
2606        id: str,
2607        state_type: StateType,
2608        trace_id: str,
2609        task_manager: TaskManager,
2610    ):
2611        """Initialize the StatefulSpanClient."""
2612        super().__init__(client, id, state_type, trace_id, task_manager)
2613
2614    # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY
2615    def update(
2616        self,
2617        *,
2618        name: typing.Optional[str] = None,
2619        start_time: typing.Optional[dt.datetime] = None,
2620        end_time: typing.Optional[dt.datetime] = None,
2621        metadata: typing.Optional[typing.Any] = None,
2622        input: typing.Optional[typing.Any] = None,
2623        output: typing.Optional[typing.Any] = None,
2624        level: typing.Optional[SpanLevel] = None,
2625        status_message: typing.Optional[str] = None,
2626        version: typing.Optional[str] = None,
2627        **kwargs,
2628    ) -> "StatefulSpanClient":
2629        """Update the span.
2630
2631        Args:
2632            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2633            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2634            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2635            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2636            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.
2637            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.
2638            input (Optional[dict]): The input to the span. Can be any JSON object.
2639            output (Optional[dict]): The output to the span. Can be any JSON object.
2640            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2641            **kwargs: Additional keyword arguments to include in the span.
2642
2643        Returns:
2644            StatefulSpanClient: The updated span. Passthrough for chaining.
2645
2646        Example:
2647            ```python
2648            from langfuse import Langfuse
2649
2650            langfuse = Langfuse()
2651
2652            # Create a trace
2653            trace = langfuse.trace(name = "llm-feature")
2654
2655            # Create a nested span in Langfuse
2656            span = trace.span(name="retrieval")
2657
2658            # Update the span
2659            span = span.update(metadata={"interface": "whatsapp"})
2660            ```
2661        """
2662        try:
2663            span_body = {
2664                "id": self.id,
2665                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2666                "name": name,
2667                "start_time": start_time,
2668                "metadata": metadata,
2669                "input": input,
2670                "output": output,
2671                "level": level,
2672                "status_message": status_message,
2673                "version": version,
2674                "end_time": end_time,
2675                **kwargs,
2676            }
2677            self.log.debug(f"Update span {span_body}...")
2678
2679            request = UpdateSpanBody(**span_body)
2680
2681            event = {
2682                "id": str(uuid.uuid4()),
2683                "type": "span-update",
2684                "body": request.dict(exclude_none=True),
2685            }
2686
2687            self.task_manager.add_task(event)
2688        except Exception as e:
2689            self.log.exception(e)
2690        finally:
2691            return StatefulSpanClient(
2692                self.client,
2693                self.id,
2694                StateType.OBSERVATION,
2695                self.trace_id,
2696                task_manager=self.task_manager,
2697            )
2698
2699    def end(
2700        self,
2701        *,
2702        name: typing.Optional[str] = None,
2703        start_time: typing.Optional[dt.datetime] = None,
2704        end_time: typing.Optional[dt.datetime] = None,
2705        metadata: typing.Optional[typing.Any] = None,
2706        input: typing.Optional[typing.Any] = None,
2707        output: typing.Optional[typing.Any] = None,
2708        level: typing.Optional[SpanLevel] = None,
2709        status_message: typing.Optional[str] = None,
2710        version: typing.Optional[str] = None,
2711        **kwargs,
2712    ) -> "StatefulSpanClient":
2713        """End the span, optionally updating its properties.
2714
2715        Args:
2716            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2717            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2718            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2719            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2720            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.
2721            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.
2722            input (Optional[dict]): The input to the span. Can be any JSON object.
2723            output (Optional[dict]): The output to the span. Can be any JSON object.
2724            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2725            **kwargs: Additional keyword arguments to include in the span.
2726
2727        Returns:
2728            StatefulSpanClient: The updated span. Passthrough for chaining.
2729
2730        Example:
2731            ```python
2732            from langfuse import Langfuse
2733
2734            langfuse = Langfuse()
2735
2736            # Create a trace
2737            trace = langfuse.trace(name = "llm-feature")
2738
2739            # Create a nested span in Langfuse
2740            span = trace.span(name="retrieval")
2741
2742            # End the span and update its properties
2743            span = span.end(metadata={"interface": "whatsapp"})
2744            ```
2745        """
2746        try:
2747            span_body = {
2748                "name": name,
2749                "start_time": start_time,
2750                "metadata": metadata,
2751                "input": input,
2752                "output": output,
2753                "level": level,
2754                "status_message": status_message,
2755                "version": version,
2756                "end_time": end_time or _get_timestamp(),
2757                **kwargs,
2758            }
2759            return self.update(**span_body)
2760
2761        except Exception as e:
2762            self.log.warning(e)
2763        finally:
2764            return StatefulSpanClient(
2765                self.client,
2766                self.id,
2767                StateType.OBSERVATION,
2768                self.trace_id,
2769                task_manager=self.task_manager,
2770            )
2771
2772    def get_langchain_handler(self, update_parent: bool = False):
2773        """Get langchain callback handler associated with the current span.
2774
2775        Args:
2776            update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
2777
2778        Returns:
2779            CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
2780        """
2781        from langfuse.callback import CallbackHandler
2782
2783        return CallbackHandler(
2784            stateful_client=self, update_stateful_client=update_parent
2785        )

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.TaskManager)
2603    def __init__(
2604        self,
2605        client: FernLangfuse,
2606        id: str,
2607        state_type: StateType,
2608        trace_id: str,
2609        task_manager: TaskManager,
2610    ):
2611        """Initialize the StatefulSpanClient."""
2612        super().__init__(client, id, state_type, trace_id, task_manager)

Initialize the StatefulSpanClient.

log = <Logger langfuse (WARNING)>
def update( self, *, name: Optional[str] = None, start_time: Optional[datetime.datetime] = None, end_time: Optional[datetime.datetime] = None, metadata: Optional[Any] = None, input: Optional[Any] = None, output: Optional[Any] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, version: Optional[str] = None, **kwargs) -> StatefulSpanClient:
2615    def update(
2616        self,
2617        *,
2618        name: typing.Optional[str] = None,
2619        start_time: typing.Optional[dt.datetime] = None,
2620        end_time: typing.Optional[dt.datetime] = None,
2621        metadata: typing.Optional[typing.Any] = None,
2622        input: typing.Optional[typing.Any] = None,
2623        output: typing.Optional[typing.Any] = None,
2624        level: typing.Optional[SpanLevel] = None,
2625        status_message: typing.Optional[str] = None,
2626        version: typing.Optional[str] = None,
2627        **kwargs,
2628    ) -> "StatefulSpanClient":
2629        """Update the span.
2630
2631        Args:
2632            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2633            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2634            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2635            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2636            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.
2637            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.
2638            input (Optional[dict]): The input to the span. Can be any JSON object.
2639            output (Optional[dict]): The output to the span. Can be any JSON object.
2640            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2641            **kwargs: Additional keyword arguments to include in the span.
2642
2643        Returns:
2644            StatefulSpanClient: The updated span. Passthrough for chaining.
2645
2646        Example:
2647            ```python
2648            from langfuse import Langfuse
2649
2650            langfuse = Langfuse()
2651
2652            # Create a trace
2653            trace = langfuse.trace(name = "llm-feature")
2654
2655            # Create a nested span in Langfuse
2656            span = trace.span(name="retrieval")
2657
2658            # Update the span
2659            span = span.update(metadata={"interface": "whatsapp"})
2660            ```
2661        """
2662        try:
2663            span_body = {
2664                "id": self.id,
2665                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2666                "name": name,
2667                "start_time": start_time,
2668                "metadata": metadata,
2669                "input": input,
2670                "output": output,
2671                "level": level,
2672                "status_message": status_message,
2673                "version": version,
2674                "end_time": end_time,
2675                **kwargs,
2676            }
2677            self.log.debug(f"Update span {span_body}...")
2678
2679            request = UpdateSpanBody(**span_body)
2680
2681            event = {
2682                "id": str(uuid.uuid4()),
2683                "type": "span-update",
2684                "body": request.dict(exclude_none=True),
2685            }
2686
2687            self.task_manager.add_task(event)
2688        except Exception as e:
2689            self.log.exception(e)
2690        finally:
2691            return StatefulSpanClient(
2692                self.client,
2693                self.id,
2694                StateType.OBSERVATION,
2695                self.trace_id,
2696                task_manager=self.task_manager,
2697            )

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:
2699    def end(
2700        self,
2701        *,
2702        name: typing.Optional[str] = None,
2703        start_time: typing.Optional[dt.datetime] = None,
2704        end_time: typing.Optional[dt.datetime] = None,
2705        metadata: typing.Optional[typing.Any] = None,
2706        input: typing.Optional[typing.Any] = None,
2707        output: typing.Optional[typing.Any] = None,
2708        level: typing.Optional[SpanLevel] = None,
2709        status_message: typing.Optional[str] = None,
2710        version: typing.Optional[str] = None,
2711        **kwargs,
2712    ) -> "StatefulSpanClient":
2713        """End the span, optionally updating its properties.
2714
2715        Args:
2716            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2717            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2718            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2719            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2720            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.
2721            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.
2722            input (Optional[dict]): The input to the span. Can be any JSON object.
2723            output (Optional[dict]): The output to the span. Can be any JSON object.
2724            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2725            **kwargs: Additional keyword arguments to include in the span.
2726
2727        Returns:
2728            StatefulSpanClient: The updated span. Passthrough for chaining.
2729
2730        Example:
2731            ```python
2732            from langfuse import Langfuse
2733
2734            langfuse = Langfuse()
2735
2736            # Create a trace
2737            trace = langfuse.trace(name = "llm-feature")
2738
2739            # Create a nested span in Langfuse
2740            span = trace.span(name="retrieval")
2741
2742            # End the span and update its properties
2743            span = span.end(metadata={"interface": "whatsapp"})
2744            ```
2745        """
2746        try:
2747            span_body = {
2748                "name": name,
2749                "start_time": start_time,
2750                "metadata": metadata,
2751                "input": input,
2752                "output": output,
2753                "level": level,
2754                "status_message": status_message,
2755                "version": version,
2756                "end_time": end_time or _get_timestamp(),
2757                **kwargs,
2758            }
2759            return self.update(**span_body)
2760
2761        except Exception as e:
2762            self.log.warning(e)
2763        finally:
2764            return StatefulSpanClient(
2765                self.client,
2766                self.id,
2767                StateType.OBSERVATION,
2768                self.trace_id,
2769                task_manager=self.task_manager,
2770            )

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):
2772    def get_langchain_handler(self, update_parent: bool = False):
2773        """Get langchain callback handler associated with the current span.
2774
2775        Args:
2776            update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
2777
2778        Returns:
2779            CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
2780        """
2781        from langfuse.callback import CallbackHandler
2782
2783        return CallbackHandler(
2784            stateful_client=self, update_stateful_client=update_parent
2785        )

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):
2788class StatefulTraceClient(StatefulClient):
2789    """Class for handling stateful operations of traces in the Langfuse system. Inherits from StatefulClient.
2790
2791    Attributes:
2792        client (FernLangfuse): Core interface for Langfuse API interaction.
2793        id (str): Unique identifier of the trace.
2794        state_type (StateType): Type of the stateful entity (observation or trace).
2795        trace_id (str): The trace ID associated with this client.
2796        task_manager (TaskManager): Manager for handling asynchronous tasks.
2797    """
2798
2799    log = logging.getLogger("langfuse")
2800
2801    def __init__(
2802        self,
2803        client: FernLangfuse,
2804        id: str,
2805        state_type: StateType,
2806        trace_id: str,
2807        task_manager: TaskManager,
2808    ):
2809        """Initialize the StatefulTraceClient."""
2810        super().__init__(client, id, state_type, trace_id, task_manager)
2811        self.task_manager = task_manager
2812
2813    def update(
2814        self,
2815        *,
2816        name: typing.Optional[str] = None,
2817        user_id: typing.Optional[str] = None,
2818        session_id: typing.Optional[str] = None,
2819        version: typing.Optional[str] = None,
2820        release: typing.Optional[str] = None,
2821        input: typing.Optional[typing.Any] = None,
2822        output: typing.Optional[typing.Any] = None,
2823        metadata: typing.Optional[typing.Any] = None,
2824        tags: typing.Optional[typing.List[str]] = None,
2825        public: typing.Optional[bool] = None,
2826        **kwargs,
2827    ) -> "StatefulTraceClient":
2828        """Update the trace.
2829
2830        Args:
2831            name: Identifier of the trace. Useful for sorting/filtering in the UI.
2832            input: The input of the trace. Can be any JSON object.
2833            output: The output of the trace. Can be any JSON object.
2834            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
2835            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
2836            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
2837            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
2838            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
2839            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.
2840            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.
2841            **kwargs: Additional keyword arguments that can be included in the trace.
2842
2843        Returns:
2844            StatefulTraceClient: The updated trace. Passthrough for chaining.
2845
2846        Example:
2847            ```python
2848            from langfuse import Langfuse
2849
2850            langfuse = Langfuse()
2851
2852            # Create a trace
2853            trace = langfuse.trace(
2854                name="example-application",
2855                user_id="user-1234")
2856            )
2857
2858            # Update the trace
2859            trace = trace.update(
2860                output={"result": "success"},
2861                metadata={"interface": "whatsapp"}
2862            )
2863            ```
2864        """
2865        try:
2866            trace_body = {
2867                "id": self.id,
2868                "name": name,
2869                "userId": user_id,
2870                "sessionId": session_id
2871                or kwargs.get("sessionId", None),  # backward compatibility
2872                "version": version,
2873                "release": release,
2874                "input": input,
2875                "output": output,
2876                "metadata": metadata,
2877                "public": public,
2878                "tags": tags,
2879                **kwargs,
2880            }
2881            self.log.debug(f"Update trace {trace_body}...")
2882
2883            request = TraceBody(**trace_body)
2884
2885            event = {
2886                "id": str(uuid.uuid4()),
2887                "type": "trace-create",
2888                "body": request.dict(exclude_none=True),
2889            }
2890
2891            self.task_manager.add_task(event)
2892
2893        except Exception as e:
2894            self.log.exception(e)
2895        finally:
2896            return StatefulTraceClient(
2897                self.client,
2898                self.id,
2899                StateType.TRACE,
2900                self.trace_id,
2901                task_manager=self.task_manager,
2902            )
2903
2904    def get_langchain_handler(self, update_parent: bool = False):
2905        """Get langchain callback handler associated with the current trace.
2906
2907        This method creates and returns a CallbackHandler instance, linking it with the current
2908        trace. Use this if you want to group multiple Langchain runs within a single trace.
2909
2910        Args:
2911            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
2912
2913        Raises:
2914            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
2915
2916        Returns:
2917            CallbackHandler: Langchain callback handler linked to the current trace.
2918
2919        Example:
2920            ```python
2921            from langfuse import Langfuse
2922
2923            langfuse = Langfuse()
2924
2925            # Create a trace
2926            trace = langfuse.trace(name = "llm-feature")
2927
2928            # Get a langchain callback handler
2929            handler = trace.get_langchain_handler()
2930            ```
2931        """
2932        try:
2933            from langfuse.callback import CallbackHandler
2934
2935            self.log.debug(f"Creating new handler for trace {self.id}")
2936
2937            return CallbackHandler(
2938                stateful_client=self,
2939                debug=self.log.level == logging.DEBUG,
2940                update_stateful_client=update_parent,
2941            )
2942        except Exception as e:
2943            self.log.exception(e)
2944
2945    def getNewHandler(self):
2946        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
2947        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.TaskManager)
2801    def __init__(
2802        self,
2803        client: FernLangfuse,
2804        id: str,
2805        state_type: StateType,
2806        trace_id: str,
2807        task_manager: TaskManager,
2808    ):
2809        """Initialize the StatefulTraceClient."""
2810        super().__init__(client, id, state_type, trace_id, task_manager)
2811        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:
2813    def update(
2814        self,
2815        *,
2816        name: typing.Optional[str] = None,
2817        user_id: typing.Optional[str] = None,
2818        session_id: typing.Optional[str] = None,
2819        version: typing.Optional[str] = None,
2820        release: typing.Optional[str] = None,
2821        input: typing.Optional[typing.Any] = None,
2822        output: typing.Optional[typing.Any] = None,
2823        metadata: typing.Optional[typing.Any] = None,
2824        tags: typing.Optional[typing.List[str]] = None,
2825        public: typing.Optional[bool] = None,
2826        **kwargs,
2827    ) -> "StatefulTraceClient":
2828        """Update the trace.
2829
2830        Args:
2831            name: Identifier of the trace. Useful for sorting/filtering in the UI.
2832            input: The input of the trace. Can be any JSON object.
2833            output: The output of the trace. Can be any JSON object.
2834            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
2835            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
2836            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
2837            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
2838            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
2839            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.
2840            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.
2841            **kwargs: Additional keyword arguments that can be included in the trace.
2842
2843        Returns:
2844            StatefulTraceClient: The updated trace. Passthrough for chaining.
2845
2846        Example:
2847            ```python
2848            from langfuse import Langfuse
2849
2850            langfuse = Langfuse()
2851
2852            # Create a trace
2853            trace = langfuse.trace(
2854                name="example-application",
2855                user_id="user-1234")
2856            )
2857
2858            # Update the trace
2859            trace = trace.update(
2860                output={"result": "success"},
2861                metadata={"interface": "whatsapp"}
2862            )
2863            ```
2864        """
2865        try:
2866            trace_body = {
2867                "id": self.id,
2868                "name": name,
2869                "userId": user_id,
2870                "sessionId": session_id
2871                or kwargs.get("sessionId", None),  # backward compatibility
2872                "version": version,
2873                "release": release,
2874                "input": input,
2875                "output": output,
2876                "metadata": metadata,
2877                "public": public,
2878                "tags": tags,
2879                **kwargs,
2880            }
2881            self.log.debug(f"Update trace {trace_body}...")
2882
2883            request = TraceBody(**trace_body)
2884
2885            event = {
2886                "id": str(uuid.uuid4()),
2887                "type": "trace-create",
2888                "body": request.dict(exclude_none=True),
2889            }
2890
2891            self.task_manager.add_task(event)
2892
2893        except Exception as e:
2894            self.log.exception(e)
2895        finally:
2896            return StatefulTraceClient(
2897                self.client,
2898                self.id,
2899                StateType.TRACE,
2900                self.trace_id,
2901                task_manager=self.task_manager,
2902            )

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):
2904    def get_langchain_handler(self, update_parent: bool = False):
2905        """Get langchain callback handler associated with the current trace.
2906
2907        This method creates and returns a CallbackHandler instance, linking it with the current
2908        trace. Use this if you want to group multiple Langchain runs within a single trace.
2909
2910        Args:
2911            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
2912
2913        Raises:
2914            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
2915
2916        Returns:
2917            CallbackHandler: Langchain callback handler linked to the current trace.
2918
2919        Example:
2920            ```python
2921            from langfuse import Langfuse
2922
2923            langfuse = Langfuse()
2924
2925            # Create a trace
2926            trace = langfuse.trace(name = "llm-feature")
2927
2928            # Get a langchain callback handler
2929            handler = trace.get_langchain_handler()
2930            ```
2931        """
2932        try:
2933            from langfuse.callback import CallbackHandler
2934
2935            self.log.debug(f"Creating new handler for trace {self.id}")
2936
2937            return CallbackHandler(
2938                stateful_client=self,
2939                debug=self.log.level == logging.DEBUG,
2940                update_stateful_client=update_parent,
2941            )
2942        except Exception as e:
2943            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):
2945    def getNewHandler(self):
2946        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
2947        return self.get_langchain_handler()

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

class DatasetItemClient:
2950class DatasetItemClient:
2951    """Class for managing dataset items in Langfuse.
2952
2953    Args:
2954        id (str): Unique identifier of the dataset item.
2955        status (DatasetStatus): The status of the dataset item. Can be either 'ACTIVE' or 'ARCHIVED'.
2956        input (Any): Input data of the dataset item.
2957        expected_output (Optional[Any]): Expected output of the dataset item.
2958        metadata (Optional[Any]): Additional metadata of the dataset item.
2959        source_trace_id (Optional[str]): Identifier of the source trace.
2960        source_observation_id (Optional[str]): Identifier of the source observation.
2961        dataset_id (str): Identifier of the dataset to which this item belongs.
2962        dataset_name (str): Name of the dataset to which this item belongs.
2963        created_at (datetime): Timestamp of dataset item creation.
2964        updated_at (datetime): Timestamp of the last update to the dataset item.
2965        langfuse (Langfuse): Instance of Langfuse client for API interactions.
2966
2967    Example:
2968        ```python
2969        from langfuse import Langfuse
2970
2971        langfuse = Langfuse()
2972
2973        dataset = langfuse.get_dataset("<dataset_name>")
2974
2975        for item in dataset.items:
2976            # Generate a completion using the input of every item
2977            completion, generation = llm_app.run(item.input)
2978
2979            # Evaluate the completion
2980            generation.score(
2981                name="example-score",
2982                value=1
2983            )
2984        ```
2985    """
2986
2987    log = logging.getLogger("langfuse")
2988
2989    id: str
2990    status: DatasetStatus
2991    input: typing.Any
2992    expected_output: typing.Optional[typing.Any]
2993    metadata: Optional[Any]
2994    source_trace_id: typing.Optional[str]
2995    source_observation_id: typing.Optional[str]
2996    dataset_id: str
2997    dataset_name: str
2998    created_at: dt.datetime
2999    updated_at: dt.datetime
3000
3001    langfuse: Langfuse
3002
3003    def __init__(self, dataset_item: DatasetItem, langfuse: Langfuse):
3004        """Initialize the DatasetItemClient."""
3005        self.id = dataset_item.id
3006        self.status = dataset_item.status
3007        self.input = dataset_item.input
3008        self.expected_output = dataset_item.expected_output
3009        self.metadata = dataset_item.metadata
3010        self.source_trace_id = dataset_item.source_trace_id
3011        self.source_observation_id = dataset_item.source_observation_id
3012        self.dataset_id = dataset_item.dataset_id
3013        self.dataset_name = dataset_item.dataset_name
3014        self.created_at = dataset_item.created_at
3015        self.updated_at = dataset_item.updated_at
3016
3017        self.langfuse = langfuse
3018
3019    def flush(self, observation: StatefulClient, run_name: str):
3020        """Flushes an observations task manager's queue.
3021
3022        Used before creating a dataset run item to ensure all events are persistent.
3023
3024        Args:
3025            observation (StatefulClient): The observation or trace client associated with the dataset item.
3026            run_name (str): The name of the dataset run.
3027        """
3028        observation.task_manager.flush()
3029
3030    def link(
3031        self,
3032        trace_or_observation: typing.Union[StatefulClient, str, None],
3033        run_name: str,
3034        run_metadata: Optional[Any] = None,
3035        run_description: Optional[str] = None,
3036        trace_id: Optional[str] = None,
3037        observation_id: Optional[str] = None,
3038    ):
3039        """Link the dataset item to observation within a specific dataset run. Creates a dataset run item.
3040
3041        Args:
3042            trace_or_observation (Union[StatefulClient, str, None]): The trace or observation object to link. Deprecated: can also be an observation ID.
3043            run_name (str): The name of the dataset run.
3044            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3045            run_description (Optional[str]): Description of the dataset run.
3046            trace_id (Optional[str]): The trace ID to link to the dataset item. Set trace_or_observation to None if trace_id is provided.
3047            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.
3048        """
3049        parsed_trace_id: str = None
3050        parsed_observation_id: str = None
3051
3052        if isinstance(trace_or_observation, StatefulClient):
3053            # flush the queue before creating the dataset run item
3054            # to ensure that all events are persisted.
3055            if trace_or_observation.state_type == StateType.TRACE:
3056                parsed_trace_id = trace_or_observation.trace_id
3057            elif trace_or_observation.state_type == StateType.OBSERVATION:
3058                parsed_observation_id = trace_or_observation.id
3059                parsed_trace_id = trace_or_observation.trace_id
3060        # legacy support for observation_id
3061        elif isinstance(trace_or_observation, str):
3062            parsed_observation_id = trace_or_observation
3063        elif trace_or_observation is None:
3064            if trace_id is not None:
3065                parsed_trace_id = trace_id
3066                if observation_id is not None:
3067                    parsed_observation_id = observation_id
3068            else:
3069                raise ValueError(
3070                    "trace_id must be provided if trace_or_observation is None"
3071                )
3072        else:
3073            raise ValueError(
3074                "trace_or_observation (arg) or trace_id (kwarg) must be provided to link the dataset item"
3075            )
3076
3077        self.log.debug(
3078            f"Creating dataset run item: {run_name} {self.id} {parsed_trace_id} {parsed_observation_id}"
3079        )
3080        self.langfuse.client.dataset_run_items.create(
3081            request=CreateDatasetRunItemRequest(
3082                runName=run_name,
3083                datasetItemId=self.id,
3084                traceId=parsed_trace_id,
3085                observationId=parsed_observation_id,
3086                metadata=run_metadata,
3087                runDescription=run_description,
3088            )
3089        )
3090
3091    def get_langchain_handler(
3092        self,
3093        *,
3094        run_name: str,
3095        run_description: Optional[str] = None,
3096        run_metadata: Optional[Any] = None,
3097    ):
3098        """Create and get a langchain callback handler linked to this dataset item.
3099
3100        Args:
3101            run_name (str): The name of the dataset run to be used in the callback handler.
3102            run_description (Optional[str]): Description of the dataset run.
3103            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3104
3105        Returns:
3106            CallbackHandler: An instance of CallbackHandler linked to the dataset item.
3107        """
3108        metadata = {
3109            "dataset_item_id": self.id,
3110            "run_name": run_name,
3111            "dataset_id": self.dataset_id,
3112        }
3113        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3114
3115        self.link(
3116            trace, run_name, run_metadata=run_metadata, run_description=run_description
3117        )
3118
3119        return trace.get_langchain_handler(update_parent=True)
3120
3121    @contextmanager
3122    def observe(
3123        self,
3124        *,
3125        run_name: str,
3126        run_description: Optional[str] = None,
3127        run_metadata: Optional[Any] = None,
3128        trace_id: Optional[str] = None,
3129    ):
3130        """Observes a dataset run within the Langfuse client.
3131
3132        Args:
3133            run_name (str): The name of the dataset run.
3134            root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created.
3135            run_description (Optional[str]): The description of the dataset run.
3136            run_metadata (Optional[Any]): Additional metadata for the dataset run.
3137
3138        Yields:
3139            StatefulTraceClient: The trace associated with the dataset run.
3140        """
3141        from langfuse.decorators import langfuse_context
3142
3143        root_trace_id = trace_id or str(uuid.uuid4())
3144
3145        langfuse_context._set_root_trace_id(root_trace_id)
3146
3147        try:
3148            yield root_trace_id
3149
3150        finally:
3151            self.link(
3152                run_name=run_name,
3153                run_metadata=run_metadata,
3154                run_description=run_description,
3155                trace_or_observation=None,
3156                trace_id=root_trace_id,
3157            )
3158
3159    @contextmanager
3160    def observe_llama_index(
3161        self,
3162        *,
3163        run_name: str,
3164        run_description: Optional[str] = None,
3165        run_metadata: Optional[Any] = None,
3166        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3167    ):
3168        """Context manager for observing LlamaIndex operations linked to this dataset item.
3169
3170        This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging
3171        and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all
3172        operations performed within the context are linked to the appropriate dataset item and run in Langfuse.
3173
3174        Args:
3175            run_name (str): The name of the dataset run.
3176            run_description (Optional[str]): Description of the dataset run. Defaults to None.
3177            run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
3178            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass
3179                to the LlamaIndex integration constructor. Defaults to an empty dictionary.
3180
3181        Yields:
3182            LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.
3183
3184        Example:
3185            ```python
3186            dataset_item = dataset.items[0]
3187
3188            with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler:
3189                # Perform LlamaIndex operations here
3190                some_llama_index_operation()
3191            ```
3192
3193        Raises:
3194            ImportError: If required modules for LlamaIndex integration are not available.
3195        """
3196        metadata = {
3197            "dataset_item_id": self.id,
3198            "run_name": run_name,
3199            "dataset_id": self.dataset_id,
3200        }
3201        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3202        self.link(
3203            trace, run_name, run_metadata=run_metadata, run_description=run_description
3204        )
3205
3206        try:
3207            import llama_index.core
3208            from llama_index.core import Settings
3209            from llama_index.core.callbacks import CallbackManager
3210
3211            from langfuse.llama_index import LlamaIndexCallbackHandler
3212
3213            callback_handler = LlamaIndexCallbackHandler(
3214                **llama_index_integration_constructor_kwargs,
3215            )
3216            callback_handler.set_root(trace, update_root=True)
3217
3218            # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler
3219            # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it
3220            prev_global_handler = llama_index.core.global_handler
3221            prev_langfuse_handler = None
3222
3223            if isinstance(prev_global_handler, LlamaIndexCallbackHandler):
3224                llama_index.core.global_handler = None
3225
3226            if Settings.callback_manager is None:
3227                Settings.callback_manager = CallbackManager([callback_handler])
3228            else:
3229                for handler in Settings.callback_manager.handlers:
3230                    if isinstance(handler, LlamaIndexCallbackHandler):
3231                        prev_langfuse_handler = handler
3232                        Settings.callback_manager.remove_handler(handler)
3233
3234                Settings.callback_manager.add_handler(callback_handler)
3235
3236        except Exception as e:
3237            self.log.exception(e)
3238
3239        try:
3240            yield callback_handler
3241        finally:
3242            # Reset the handlers
3243            Settings.callback_manager.remove_handler(callback_handler)
3244            if prev_langfuse_handler is not None:
3245                Settings.callback_manager.add_handler(prev_langfuse_handler)
3246
3247            llama_index.core.global_handler = prev_global_handler
3248
3249    def get_llama_index_handler(
3250        self,
3251        *,
3252        run_name: str,
3253        run_description: Optional[str] = None,
3254        run_metadata: Optional[Any] = None,
3255        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3256    ):
3257        """Create and get a llama-index callback handler linked to this dataset item.
3258
3259        Args:
3260            run_name (str): The name of the dataset run to be used in the callback handler.
3261            run_description (Optional[str]): Description of the dataset run.
3262            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3263            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
3264
3265        Returns:
3266            LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.
3267        """
3268        metadata = {
3269            "dataset_item_id": self.id,
3270            "run_name": run_name,
3271            "dataset_id": self.dataset_id,
3272        }
3273        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3274
3275        self.link(
3276            trace, run_name, run_metadata=run_metadata, run_description=run_description
3277        )
3278
3279        try:
3280            from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler
3281
3282            callback_handler = LlamaIndexCallbackHandler(
3283                **llama_index_integration_constructor_kwargs,
3284            )
3285            callback_handler.set_root(trace, update_root=True)
3286
3287            return callback_handler
3288        except Exception as e:
3289            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)
3003    def __init__(self, dataset_item: DatasetItem, langfuse: Langfuse):
3004        """Initialize the DatasetItemClient."""
3005        self.id = dataset_item.id
3006        self.status = dataset_item.status
3007        self.input = dataset_item.input
3008        self.expected_output = dataset_item.expected_output
3009        self.metadata = dataset_item.metadata
3010        self.source_trace_id = dataset_item.source_trace_id
3011        self.source_observation_id = dataset_item.source_observation_id
3012        self.dataset_id = dataset_item.dataset_id
3013        self.dataset_name = dataset_item.dataset_name
3014        self.created_at = dataset_item.created_at
3015        self.updated_at = dataset_item.updated_at
3016
3017        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):
3019    def flush(self, observation: StatefulClient, run_name: str):
3020        """Flushes an observations task manager's queue.
3021
3022        Used before creating a dataset run item to ensure all events are persistent.
3023
3024        Args:
3025            observation (StatefulClient): The observation or trace client associated with the dataset item.
3026            run_name (str): The name of the dataset run.
3027        """
3028        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):
3091    def get_langchain_handler(
3092        self,
3093        *,
3094        run_name: str,
3095        run_description: Optional[str] = None,
3096        run_metadata: Optional[Any] = None,
3097    ):
3098        """Create and get a langchain callback handler linked to this dataset item.
3099
3100        Args:
3101            run_name (str): The name of the dataset run to be used in the callback handler.
3102            run_description (Optional[str]): Description of the dataset run.
3103            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3104
3105        Returns:
3106            CallbackHandler: An instance of CallbackHandler linked to the dataset item.
3107        """
3108        metadata = {
3109            "dataset_item_id": self.id,
3110            "run_name": run_name,
3111            "dataset_id": self.dataset_id,
3112        }
3113        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3114
3115        self.link(
3116            trace, run_name, run_metadata=run_metadata, run_description=run_description
3117        )
3118
3119        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):
3121    @contextmanager
3122    def observe(
3123        self,
3124        *,
3125        run_name: str,
3126        run_description: Optional[str] = None,
3127        run_metadata: Optional[Any] = None,
3128        trace_id: Optional[str] = None,
3129    ):
3130        """Observes a dataset run within the Langfuse client.
3131
3132        Args:
3133            run_name (str): The name of the dataset run.
3134            root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created.
3135            run_description (Optional[str]): The description of the dataset run.
3136            run_metadata (Optional[Any]): Additional metadata for the dataset run.
3137
3138        Yields:
3139            StatefulTraceClient: The trace associated with the dataset run.
3140        """
3141        from langfuse.decorators import langfuse_context
3142
3143        root_trace_id = trace_id or str(uuid.uuid4())
3144
3145        langfuse_context._set_root_trace_id(root_trace_id)
3146
3147        try:
3148            yield root_trace_id
3149
3150        finally:
3151            self.link(
3152                run_name=run_name,
3153                run_metadata=run_metadata,
3154                run_description=run_description,
3155                trace_or_observation=None,
3156                trace_id=root_trace_id,
3157            )

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]] = {}):
3159    @contextmanager
3160    def observe_llama_index(
3161        self,
3162        *,
3163        run_name: str,
3164        run_description: Optional[str] = None,
3165        run_metadata: Optional[Any] = None,
3166        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3167    ):
3168        """Context manager for observing LlamaIndex operations linked to this dataset item.
3169
3170        This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging
3171        and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all
3172        operations performed within the context are linked to the appropriate dataset item and run in Langfuse.
3173
3174        Args:
3175            run_name (str): The name of the dataset run.
3176            run_description (Optional[str]): Description of the dataset run. Defaults to None.
3177            run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
3178            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass
3179                to the LlamaIndex integration constructor. Defaults to an empty dictionary.
3180
3181        Yields:
3182            LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.
3183
3184        Example:
3185            ```python
3186            dataset_item = dataset.items[0]
3187
3188            with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler:
3189                # Perform LlamaIndex operations here
3190                some_llama_index_operation()
3191            ```
3192
3193        Raises:
3194            ImportError: If required modules for LlamaIndex integration are not available.
3195        """
3196        metadata = {
3197            "dataset_item_id": self.id,
3198            "run_name": run_name,
3199            "dataset_id": self.dataset_id,
3200        }
3201        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3202        self.link(
3203            trace, run_name, run_metadata=run_metadata, run_description=run_description
3204        )
3205
3206        try:
3207            import llama_index.core
3208            from llama_index.core import Settings
3209            from llama_index.core.callbacks import CallbackManager
3210
3211            from langfuse.llama_index import LlamaIndexCallbackHandler
3212
3213            callback_handler = LlamaIndexCallbackHandler(
3214                **llama_index_integration_constructor_kwargs,
3215            )
3216            callback_handler.set_root(trace, update_root=True)
3217
3218            # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler
3219            # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it
3220            prev_global_handler = llama_index.core.global_handler
3221            prev_langfuse_handler = None
3222
3223            if isinstance(prev_global_handler, LlamaIndexCallbackHandler):
3224                llama_index.core.global_handler = None
3225
3226            if Settings.callback_manager is None:
3227                Settings.callback_manager = CallbackManager([callback_handler])
3228            else:
3229                for handler in Settings.callback_manager.handlers:
3230                    if isinstance(handler, LlamaIndexCallbackHandler):
3231                        prev_langfuse_handler = handler
3232                        Settings.callback_manager.remove_handler(handler)
3233
3234                Settings.callback_manager.add_handler(callback_handler)
3235
3236        except Exception as e:
3237            self.log.exception(e)
3238
3239        try:
3240            yield callback_handler
3241        finally:
3242            # Reset the handlers
3243            Settings.callback_manager.remove_handler(callback_handler)
3244            if prev_langfuse_handler is not None:
3245                Settings.callback_manager.add_handler(prev_langfuse_handler)
3246
3247            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]] = {}):
3249    def get_llama_index_handler(
3250        self,
3251        *,
3252        run_name: str,
3253        run_description: Optional[str] = None,
3254        run_metadata: Optional[Any] = None,
3255        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3256    ):
3257        """Create and get a llama-index callback handler linked to this dataset item.
3258
3259        Args:
3260            run_name (str): The name of the dataset run to be used in the callback handler.
3261            run_description (Optional[str]): Description of the dataset run.
3262            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3263            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
3264
3265        Returns:
3266            LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.
3267        """
3268        metadata = {
3269            "dataset_item_id": self.id,
3270            "run_name": run_name,
3271            "dataset_id": self.dataset_id,
3272        }
3273        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3274
3275        self.link(
3276            trace, run_name, run_metadata=run_metadata, run_description=run_description
3277        )
3278
3279        try:
3280            from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler
3281
3282            callback_handler = LlamaIndexCallbackHandler(
3283                **llama_index_integration_constructor_kwargs,
3284            )
3285            callback_handler.set_root(trace, update_root=True)
3286
3287            return callback_handler
3288        except Exception as e:
3289            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:
3292class DatasetClient:
3293    """Class for managing datasets in Langfuse.
3294
3295    Attributes:
3296        id (str): Unique identifier of the dataset.
3297        name (str): Name of the dataset.
3298        description (Optional[str]): Description of the dataset.
3299        metadata (Optional[typing.Any]): Additional metadata of the dataset.
3300        project_id (str): Identifier of the project to which the dataset belongs.
3301        dataset_name (str): Name of the dataset.
3302        created_at (datetime): Timestamp of dataset creation.
3303        updated_at (datetime): Timestamp of the last update to the dataset.
3304        items (List[DatasetItemClient]): List of dataset items associated with the dataset.
3305        runs (List[str]): List of dataset runs associated with the dataset. Deprecated.
3306
3307    Example:
3308        Print the input of each dataset item in a dataset.
3309        ```python
3310        from langfuse import Langfuse
3311
3312        langfuse = Langfuse()
3313
3314        dataset = langfuse.get_dataset("<dataset_name>")
3315
3316        for item in dataset.items:
3317            print(item.input)
3318        ```
3319    """
3320
3321    id: str
3322    name: str
3323    description: Optional[str]
3324    project_id: str
3325    dataset_name: str  # for backward compatibility, to be deprecated
3326    metadata: Optional[Any]
3327    created_at: dt.datetime
3328    updated_at: dt.datetime
3329    items: typing.List[DatasetItemClient]
3330    runs: typing.List[str] = []  # deprecated
3331
3332    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3333        """Initialize the DatasetClient."""
3334        self.id = dataset.id
3335        self.name = dataset.name
3336        self.description = dataset.description
3337        self.project_id = dataset.project_id
3338        self.metadata = dataset.metadata
3339        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3340        self.created_at = dataset.created_at
3341        self.updated_at = dataset.updated_at
3342        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])
3332    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3333        """Initialize the DatasetClient."""
3334        self.id = dataset.id
3335        self.name = dataset.name
3336        self.description = dataset.description
3337        self.project_id = dataset.project_id
3338        self.metadata = dataset.metadata
3339        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3340        self.created_at = dataset.created_at
3341        self.updated_at = dataset.updated_at
3342        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] = []