langfuse.client

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

Response object for fetch_traces method.

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

Response object for fetch_trace method.

FetchTraceResponse( data: langfuse.api.resources.commons.types.trace_with_full_details.TraceWithFullDetails)
data: langfuse.api.resources.commons.types.trace_with_full_details.TraceWithFullDetails
@dataclass
class FetchObservationsResponse:
107@dataclass
108class FetchObservationsResponse:
109    """Response object for fetch_observations method."""
110
111    data: typing.List[ObservationsView]
112    meta: MetaResponse

Response object for fetch_observations method.

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

Response object for fetch_observation method.

FetchObservationResponse(data: langfuse.api.resources.commons.types.observation.Observation)
data: langfuse.api.resources.commons.types.observation.Observation
@dataclass
class FetchSessionsResponse:
122@dataclass
123class FetchSessionsResponse:
124    """Response object for fetch_sessions method."""
125
126    data: typing.List[Session]
127    meta: MetaResponse

Response object for fetch_sessions method.

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

Get the current trace id.

def get_trace_url(self) -> str:
317    def get_trace_url(self) -> str:
318        """Get the URL of the current trace to view it in the Langfuse UI."""
319        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:
321    def get_dataset(
322        self, name: str, *, fetch_items_page_size: Optional[int] = 50
323    ) -> "DatasetClient":
324        """Fetch a dataset by its name.
325
326        Args:
327            name (str): The name of the dataset to fetch.
328            fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
329
330        Returns:
331            DatasetClient: The dataset with the given name.
332        """
333        try:
334            self.log.debug(f"Getting datasets {name}")
335            dataset = self.client.datasets.get(dataset_name=name)
336
337            dataset_items = []
338            page = 1
339            while True:
340                new_items = self.client.dataset_items.list(
341                    dataset_name=name, page=page, limit=fetch_items_page_size
342                )
343                dataset_items.extend(new_items.data)
344                if new_items.meta.total_pages <= page:
345                    break
346                page += 1
347
348            items = [DatasetItemClient(i, langfuse=self) for i in dataset_items]
349
350            return DatasetClient(dataset, items=items)
351        except Exception as e:
352            self.log.exception(e)
353            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:
355    def get_dataset_item(self, id: str) -> "DatasetItemClient":
356        """Get the dataset item with the given id."""
357        try:
358            self.log.debug(f"Getting dataset item {id}")
359            dataset_item = self.client.dataset_items.get(id=id)
360            return DatasetItemClient(dataset_item, langfuse=self)
361        except Exception as e:
362            self.log.exception(e)
363            raise e

Get the dataset item with the given id.

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

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:
836    def fetch_observation(
837        self,
838        id: str,
839    ) -> FetchObservationResponse:
840        """Get an observation in the current project with the given identifier.
841
842        Args:
843            id: The identifier of the observation to fetch.
844
845        Returns:
846            FetchObservationResponse: The observation with the given id on `data`.
847
848        Raises:
849            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
850        """
851        try:
852            self.log.debug(f"Getting observation {id}")
853            observation = self.client.observations.get(id)
854            return FetchObservationResponse(data=observation)
855        except Exception as e:
856            self.log.exception(e)
857            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.resources.commons.types.observation.Observation:
859    def get_observation(
860        self,
861        id: str,
862    ) -> Observation:
863        """Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead.
864
865        Args:
866            id: The identifier of the observation to fetch.
867
868        Raises:
869            Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
870        """
871        warnings.warn(
872            "get_observation is deprecated, use fetch_observation instead.",
873            DeprecationWarning,
874        )
875        try:
876            self.log.debug(f"Getting observation {id}")
877            return self.client.observations.get(id)
878        except Exception as e:
879            self.log.exception(e)
880            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:
882    def fetch_sessions(
883        self,
884        *,
885        page: typing.Optional[int] = None,
886        limit: typing.Optional[int] = None,
887        from_timestamp: typing.Optional[dt.datetime] = None,
888        to_timestamp: typing.Optional[dt.datetime] = None,
889    ) -> FetchSessionsResponse:
890        """Get a list of sessions in the current project.
891
892        Args:
893            page (Optional[int]): Page number of the sessions to return. Defaults to None.
894            limit (Optional[int]): Maximum number of sessions to return. Defaults to None.
895            from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None.
896            to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None.
897
898        Returns:
899            FetchSessionsResponse, list of sessions on `data` and metadata on `meta`.
900
901        Raises:
902            Exception: If an error occurred during the request.
903        """
904        try:
905            self.log.debug(
906                f"Getting sessions... {page}, {limit}, {from_timestamp}, {to_timestamp}"
907            )
908            res = self.client.sessions.list(
909                page=page,
910                limit=limit,
911                from_timestamp=from_timestamp,
912                to_timestamp=to_timestamp,
913            )
914            return FetchSessionsResponse(data=res.data, meta=res.meta)
915        except Exception as e:
916            self.log.exception(e)
917            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) -> Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient]:
 945    def get_prompt(
 946        self,
 947        name: str,
 948        version: Optional[int] = None,
 949        *,
 950        label: Optional[str] = None,
 951        type: Literal["chat", "text"] = "text",
 952        cache_ttl_seconds: Optional[int] = None,
 953        fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None,
 954        max_retries: Optional[int] = None,
 955    ) -> PromptClient:
 956        """Get a prompt.
 957
 958        This method attempts to fetch the requested prompt from the local cache. If the prompt is not found
 959        in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again
 960        and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will
 961        return the expired prompt as a fallback.
 962
 963        Args:
 964            name (str): The name of the prompt to retrieve.
 965
 966        Keyword Args:
 967            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.
 968            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.
 969            cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a
 970            keyword argument. If not set, defaults to 60 seconds.
 971            type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text".
 972            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.
 973            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.
 974
 975        Returns:
 976            The prompt object retrieved from the cache or directly fetched if not cached or expired of type
 977            - TextPromptClient, if type argument is 'text'.
 978            - ChatPromptClient, if type argument is 'chat'.
 979
 980        Raises:
 981            Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
 982            expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
 983        """
 984        if version is not None and label is not None:
 985            raise ValueError("Cannot specify both version and label at the same time.")
 986
 987        if not name:
 988            raise ValueError("Prompt name cannot be empty.")
 989
 990        cache_key = PromptCache.generate_cache_key(name, version=version, label=label)
 991        bounded_max_retries = self._get_bounded_max_retries(
 992            max_retries, default_max_retries=2, max_retries_upper_bound=4
 993        )
 994
 995        self.log.debug(f"Getting prompt '{cache_key}'")
 996        cached_prompt = self.prompt_cache.get(cache_key)
 997
 998        if cached_prompt is None:
 999            self.log.debug(f"Prompt '{cache_key}' not found in cache.")
1000            try:
1001                return self._fetch_prompt_and_update_cache(
1002                    name,
1003                    version=version,
1004                    label=label,
1005                    ttl_seconds=cache_ttl_seconds,
1006                    max_retries=bounded_max_retries,
1007                )
1008            except Exception as e:
1009                if fallback:
1010                    self.log.warn(
1011                        f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}"
1012                    )
1013
1014                    fallback_client_args = {
1015                        "name": name,
1016                        "prompt": fallback,
1017                        "type": type,
1018                        "version": version or 0,
1019                        "config": {},
1020                        "labels": [label] if label else [],
1021                        "tags": [],
1022                    }
1023
1024                    if type == "text":
1025                        return TextPromptClient(
1026                            prompt=Prompt_Text(**fallback_client_args),
1027                            is_fallback=True,
1028                        )
1029
1030                    if type == "chat":
1031                        return ChatPromptClient(
1032                            prompt=Prompt_Chat(**fallback_client_args),
1033                            is_fallback=True,
1034                        )
1035
1036                raise e
1037
1038        if cached_prompt.is_expired():
1039            try:
1040                return self._fetch_prompt_and_update_cache(
1041                    name,
1042                    version=version,
1043                    label=label,
1044                    ttl_seconds=cache_ttl_seconds,
1045                    max_retries=bounded_max_retries,
1046                )
1047
1048            except Exception as e:
1049                self.log.warn(
1050                    f"Returning expired prompt cache for '{cache_key}' due to fetch error: {e}"
1051                )
1052
1053                return cached_prompt.value
1054
1055        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. 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.

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]:
1135    def create_prompt(
1136        self,
1137        *,
1138        name: str,
1139        prompt: Union[str, List[ChatMessageDict]],
1140        is_active: Optional[bool] = None,  # deprecated
1141        labels: List[str] = [],
1142        tags: Optional[List[str]] = None,
1143        type: Optional[Literal["chat", "text"]] = "text",
1144        config: Optional[Any] = None,
1145    ) -> PromptClient:
1146        """Create a new prompt in Langfuse.
1147
1148        Keyword Args:
1149            name : The name of the prompt to be created.
1150            prompt : The content of the prompt to be created.
1151            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.
1152            labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label.
1153            tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt.
1154            config: Additional structured data to be saved with the prompt. Defaults to None.
1155            type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text".
1156
1157        Returns:
1158            TextPromptClient: The prompt if type argument is 'text'.
1159            ChatPromptClient: The prompt if type argument is 'chat'.
1160        """
1161        try:
1162            self.log.debug(f"Creating prompt {name=}, {version=}, {labels=}")
1163
1164            # Handle deprecated is_active flag
1165            if is_active:
1166                self.log.warning(
1167                    "The 'is_active' flag is deprecated and will be removed in a future release. Please use the 'production' label instead."
1168                )
1169
1170                labels = labels if "production" in labels else labels + ["production"]
1171
1172            if type == "chat":
1173                if not isinstance(prompt, list):
1174                    raise ValueError(
1175                        "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes."
1176                    )
1177                request = CreatePromptRequest_Chat(
1178                    name=name,
1179                    prompt=prompt,
1180                    labels=labels,
1181                    tags=tags,
1182                    config=config or {},
1183                    type="chat",
1184                )
1185                server_prompt = self.client.prompts.create(request=request)
1186
1187                return ChatPromptClient(prompt=server_prompt)
1188
1189            if not isinstance(prompt, str):
1190                raise ValueError("For 'text' type, 'prompt' must be a string.")
1191
1192            request = CreatePromptRequest_Text(
1193                name=name,
1194                prompt=prompt,
1195                labels=labels,
1196                tags=tags,
1197                config=config or {},
1198                type="text",
1199            )
1200
1201            server_prompt = self.client.prompts.create(request=request)
1202            return TextPromptClient(prompt=server_prompt)
1203
1204        except Exception as e:
1205            self.log.exception(e)
1206            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:
1211    def trace(
1212        self,
1213        *,
1214        id: typing.Optional[str] = None,
1215        name: typing.Optional[str] = None,
1216        user_id: typing.Optional[str] = None,
1217        session_id: typing.Optional[str] = None,
1218        version: typing.Optional[str] = None,
1219        input: typing.Optional[typing.Any] = None,
1220        output: typing.Optional[typing.Any] = None,
1221        metadata: typing.Optional[typing.Any] = None,
1222        tags: typing.Optional[typing.List[str]] = None,
1223        timestamp: typing.Optional[dt.datetime] = None,
1224        public: typing.Optional[bool] = None,
1225        **kwargs,
1226    ) -> "StatefulTraceClient":
1227        """Create a trace.
1228
1229        Args:
1230            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.
1231            name: Identifier of the trace. Useful for sorting/filtering in the UI.
1232            input: The input of the trace. Can be any JSON object.
1233            output: The output of the trace. Can be any JSON object.
1234            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
1235            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
1236            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
1237            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
1238            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
1239            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.
1240            timestamp: The timestamp of the trace. Defaults to the current time if not provided.
1241            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.
1242            **kwargs: Additional keyword arguments that can be included in the trace.
1243
1244        Returns:
1245            StatefulTraceClient: The created trace.
1246
1247        Example:
1248            ```python
1249            from langfuse import Langfuse
1250
1251            langfuse = Langfuse()
1252
1253            trace = langfuse.trace(
1254                name="example-application",
1255                user_id="user-1234")
1256            )
1257            ```
1258        """
1259        new_id = id or str(uuid.uuid4())
1260        self.trace_id = new_id
1261        try:
1262            new_dict = {
1263                "id": new_id,
1264                "name": name,
1265                "userId": user_id,
1266                "sessionId": session_id
1267                or kwargs.get("sessionId", None),  # backward compatibility
1268                "release": self.release,
1269                "version": version,
1270                "metadata": metadata,
1271                "input": input,
1272                "output": output,
1273                "tags": tags,
1274                "timestamp": timestamp or _get_timestamp(),
1275                "public": public,
1276            }
1277            if kwargs is not None:
1278                new_dict.update(kwargs)
1279
1280            new_body = TraceBody(**new_dict)
1281
1282            self.log.debug(f"Creating trace {new_body}")
1283            event = {
1284                "id": str(uuid.uuid4()),
1285                "type": "trace-create",
1286                "body": new_body.dict(exclude_none=True),
1287            }
1288
1289            self.task_manager.add_task(
1290                event,
1291            )
1292
1293        except Exception as e:
1294            self.log.exception(e)
1295        finally:
1296            self._log_memory_usage()
1297
1298            return StatefulTraceClient(
1299                self.client, new_id, StateType.TRACE, new_id, self.task_manager
1300            )

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:
1379    def score(
1380        self,
1381        *,
1382        name: str,
1383        value: typing.Union[float, str],
1384        data_type: typing.Optional[ScoreDataType] = None,
1385        trace_id: typing.Optional[str] = None,
1386        id: typing.Optional[str] = None,
1387        comment: typing.Optional[str] = None,
1388        observation_id: typing.Optional[str] = None,
1389        config_id: typing.Optional[str] = None,
1390        **kwargs,
1391    ) -> "StatefulClient":
1392        """Create a score attached to a trace (and optionally an observation).
1393
1394        Args:
1395            name (str): Identifier of the score.
1396            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.
1397            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.
1398              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.
1399            trace_id (str): The id of the trace to which the score should be attached.
1400            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
1401            comment (Optional[str]): Additional context/explanation of the score.
1402            observation_id (Optional[str]): The id of the observation to which the score should be attached.
1403            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
1404            **kwargs: Additional keyword arguments to include in the score.
1405
1406        Returns:
1407            StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided).
1408
1409        Example:
1410            ```python
1411            from langfuse import Langfuse
1412
1413            langfuse = Langfuse()
1414
1415            # Create a trace
1416            trace = langfuse.trace(name="example-application")
1417
1418            # Get id of created trace
1419            trace_id = trace.id
1420
1421            # Add score to the trace
1422            trace = langfuse.score(
1423                trace_id=trace_id,
1424                name="user-explicit-feedback",
1425                value=0.9,
1426                comment="I like how personalized the response is"
1427            )
1428            ```
1429        """
1430        trace_id = trace_id or self.trace_id or str(uuid.uuid4())
1431        new_id = id or str(uuid.uuid4())
1432        try:
1433            new_dict = {
1434                "id": new_id,
1435                "trace_id": trace_id,
1436                "observation_id": observation_id,
1437                "name": name,
1438                "value": value,
1439                "data_type": data_type,
1440                "comment": comment,
1441                "config_id": config_id,
1442                **kwargs,
1443            }
1444
1445            self.log.debug(f"Creating score {new_dict}...")
1446            new_body = ScoreBody(**new_dict)
1447
1448            event = {
1449                "id": str(uuid.uuid4()),
1450                "type": "score-create",
1451                "body": new_body.dict(exclude_none=True),
1452            }
1453            self.task_manager.add_task(event)
1454
1455        except Exception as e:
1456            self.log.exception(e)
1457        finally:
1458            if observation_id is not None:
1459                return StatefulClient(
1460                    self.client,
1461                    observation_id,
1462                    StateType.OBSERVATION,
1463                    trace_id,
1464                    self.task_manager,
1465                )
1466            else:
1467                return StatefulClient(
1468                    self.client, new_id, StateType.TRACE, new_id, self.task_manager
1469                )

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:
1471    def span(
1472        self,
1473        *,
1474        id: typing.Optional[str] = None,
1475        trace_id: typing.Optional[str] = None,
1476        parent_observation_id: typing.Optional[str] = None,
1477        name: typing.Optional[str] = None,
1478        start_time: typing.Optional[dt.datetime] = None,
1479        end_time: typing.Optional[dt.datetime] = None,
1480        metadata: typing.Optional[typing.Any] = None,
1481        level: typing.Optional[SpanLevel] = None,
1482        status_message: typing.Optional[str] = None,
1483        input: typing.Optional[typing.Any] = None,
1484        output: typing.Optional[typing.Any] = None,
1485        version: typing.Optional[str] = None,
1486        **kwargs,
1487    ) -> "StatefulSpanClient":
1488        """Create a span.
1489
1490        A span represents durations of units of work in a trace.
1491        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.
1492
1493        If no trace_id is provided, a new trace is created just for this span.
1494
1495        Args:
1496            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
1497            trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated.
1498            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1499            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
1500            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
1501            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
1502            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
1503            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.
1504            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.
1505            input (Optional[dict]): The input to the span. Can be any JSON object.
1506            output (Optional[dict]): The output to the span. Can be any JSON object.
1507            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1508            **kwargs: Additional keyword arguments to include in the span.
1509
1510        Returns:
1511            StatefulSpanClient: The created span.
1512
1513        Example:
1514            ```python
1515            from langfuse import Langfuse
1516
1517            langfuse = Langfuse()
1518
1519            trace = langfuse.trace(name = "llm-feature")
1520
1521            # Create a span
1522            retrieval = langfuse.span(name = "retrieval", trace_id = trace.id)
1523
1524            # Create a nested span
1525            nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id)
1526            ```
1527        """
1528        new_span_id = id or str(uuid.uuid4())
1529        new_trace_id = trace_id or str(uuid.uuid4())
1530        self.trace_id = new_trace_id
1531        try:
1532            span_body = {
1533                "id": new_span_id,
1534                "trace_id": new_trace_id,
1535                "name": name,
1536                "start_time": start_time or _get_timestamp(),
1537                "metadata": metadata,
1538                "input": input,
1539                "output": output,
1540                "level": level,
1541                "status_message": status_message,
1542                "parent_observation_id": parent_observation_id,
1543                "version": version,
1544                "end_time": end_time,
1545                "trace": {"release": self.release},
1546                **kwargs,
1547            }
1548
1549            if trace_id is None:
1550                self._generate_trace(new_trace_id, name or new_trace_id)
1551
1552            self.log.debug(f"Creating span {span_body}...")
1553
1554            span_body = CreateSpanBody(**span_body)
1555
1556            event = {
1557                "id": str(uuid.uuid4()),
1558                "type": "span-create",
1559                "body": span_body.dict(exclude_none=True),
1560            }
1561
1562            self.log.debug(f"Creating span {event}...")
1563            self.task_manager.add_task(event)
1564
1565        except Exception as e:
1566            self.log.exception(e)
1567        finally:
1568            self._log_memory_usage()
1569
1570            return StatefulSpanClient(
1571                self.client,
1572                new_span_id,
1573                StateType.OBSERVATION,
1574                new_trace_id,
1575                self.task_manager,
1576            )

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:
1578    def event(
1579        self,
1580        *,
1581        id: typing.Optional[str] = None,
1582        trace_id: typing.Optional[str] = None,
1583        parent_observation_id: typing.Optional[str] = None,
1584        name: typing.Optional[str] = None,
1585        start_time: typing.Optional[dt.datetime] = None,
1586        metadata: typing.Optional[typing.Any] = None,
1587        input: typing.Optional[typing.Any] = None,
1588        output: typing.Optional[typing.Any] = None,
1589        level: typing.Optional[SpanLevel] = None,
1590        status_message: typing.Optional[str] = None,
1591        version: typing.Optional[str] = None,
1592        **kwargs,
1593    ) -> "StatefulSpanClient":
1594        """Create an event.
1595
1596        An event represents a discrete event in a trace.
1597        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.
1598
1599        If no trace_id is provided, a new trace is created just for this event.
1600
1601        Args:
1602            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
1603            trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event.
1604            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1605            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
1606            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
1607            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
1608            input (Optional[Any]): The input to the event. Can be any JSON object.
1609            output (Optional[Any]): The output to the event. Can be any JSON object.
1610            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.
1611            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.
1612            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
1613            **kwargs: Additional keyword arguments to include in the event.
1614
1615        Returns:
1616            StatefulSpanClient: The created event.
1617
1618        Example:
1619            ```python
1620            from langfuse import Langfuse
1621
1622            langfuse = Langfuse()
1623
1624            trace = langfuse.trace(name = "llm-feature")
1625
1626            # Create an event
1627            retrieval = langfuse.event(name = "retrieval", trace_id = trace.id)
1628            ```
1629        """
1630        event_id = id or str(uuid.uuid4())
1631        new_trace_id = trace_id or str(uuid.uuid4())
1632        self.trace_id = new_trace_id
1633        try:
1634            event_body = {
1635                "id": event_id,
1636                "trace_id": new_trace_id,
1637                "name": name,
1638                "start_time": start_time or _get_timestamp(),
1639                "metadata": metadata,
1640                "input": input,
1641                "output": output,
1642                "level": level,
1643                "status_message": status_message,
1644                "parent_observation_id": parent_observation_id,
1645                "version": version,
1646                "trace": {"release": self.release},
1647                **kwargs,
1648            }
1649
1650            if trace_id is None:
1651                self._generate_trace(new_trace_id, name or new_trace_id)
1652
1653            request = CreateEventBody(**event_body)
1654
1655            event = {
1656                "id": str(uuid.uuid4()),
1657                "type": "event-create",
1658                "body": request.dict(exclude_none=True),
1659            }
1660
1661            self.log.debug(f"Creating event {event}...")
1662            self.task_manager.add_task(event)
1663
1664        except Exception as e:
1665            self.log.exception(e)
1666        finally:
1667            return StatefulSpanClient(
1668                self.client,
1669                event_id,
1670                StateType.OBSERVATION,
1671                new_trace_id,
1672                self.task_manager,
1673            )

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:
1675    def generation(
1676        self,
1677        *,
1678        id: typing.Optional[str] = None,
1679        trace_id: typing.Optional[str] = None,
1680        parent_observation_id: typing.Optional[str] = None,
1681        name: typing.Optional[str] = None,
1682        start_time: typing.Optional[dt.datetime] = None,
1683        end_time: typing.Optional[dt.datetime] = None,
1684        completion_start_time: typing.Optional[dt.datetime] = None,
1685        metadata: typing.Optional[typing.Any] = None,
1686        level: typing.Optional[SpanLevel] = None,
1687        status_message: typing.Optional[str] = None,
1688        version: typing.Optional[str] = None,
1689        model: typing.Optional[str] = None,
1690        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
1691        input: typing.Optional[typing.Any] = None,
1692        output: typing.Optional[typing.Any] = None,
1693        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
1694        prompt: typing.Optional[PromptClient] = None,
1695        **kwargs,
1696    ) -> "StatefulGenerationClient":
1697        """Create a generation.
1698
1699        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.
1700
1701        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.
1702
1703        If no trace_id is provided, a new trace is created just for this generation.
1704
1705        Args:
1706            id (Optional[str]): The id of the generation can be set, defaults to random id.
1707            trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created
1708            parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
1709            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
1710            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
1711            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
1712            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.
1713            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
1714            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.
1715            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.
1716            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
1717            model (Optional[str]): The name of the model used for the generation.
1718            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
1719            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
1720            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
1721            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.
1722            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
1723            **kwargs: Additional keyword arguments to include in the generation.
1724
1725        Returns:
1726            StatefulGenerationClient: The created generation.
1727
1728        Example:
1729            ```python
1730            from langfuse import Langfuse
1731
1732            langfuse = Langfuse()
1733
1734            # Create a generation in Langfuse
1735            generation = langfuse.generation(
1736                name="summary-generation",
1737                model="gpt-3.5-turbo",
1738                model_parameters={"maxTokens": "1000", "temperature": "0.9"},
1739                input=[{"role": "system", "content": "You are a helpful assistant."},
1740                       {"role": "user", "content": "Please generate a summary of the following documents ..."}],
1741                metadata={"interface": "whatsapp"}
1742            )
1743            ```
1744        """
1745        new_trace_id = trace_id or str(uuid.uuid4())
1746        new_generation_id = id or str(uuid.uuid4())
1747        self.trace_id = new_trace_id
1748        try:
1749            generation_body = {
1750                "id": new_generation_id,
1751                "trace_id": new_trace_id,
1752                "release": self.release,
1753                "name": name,
1754                "start_time": start_time or _get_timestamp(),
1755                "metadata": metadata,
1756                "input": input,
1757                "output": output,
1758                "level": level,
1759                "status_message": status_message,
1760                "parent_observation_id": parent_observation_id,
1761                "version": version,
1762                "end_time": end_time,
1763                "completion_start_time": completion_start_time,
1764                "model": model,
1765                "model_parameters": model_parameters,
1766                "usage": _convert_usage_input(usage) if usage is not None else None,
1767                "trace": {"release": self.release},
1768                **_create_prompt_context(prompt),
1769                **kwargs,
1770            }
1771
1772            if trace_id is None:
1773                trace = {
1774                    "id": new_trace_id,
1775                    "release": self.release,
1776                    "name": name,
1777                }
1778                request = TraceBody(**trace)
1779
1780                event = {
1781                    "id": str(uuid.uuid4()),
1782                    "type": "trace-create",
1783                    "body": request.dict(exclude_none=True),
1784                }
1785
1786                self.log.debug(f"Creating trace {event}...")
1787
1788                self.task_manager.add_task(event)
1789
1790            self.log.debug(f"Creating generation max {generation_body} {usage}...")
1791            request = CreateGenerationBody(**generation_body)
1792
1793            event = {
1794                "id": str(uuid.uuid4()),
1795                "type": "generation-create",
1796                "body": request.dict(exclude_none=True),
1797            }
1798
1799            self.log.debug(f"Creating top-level generation {event} ...")
1800            self.task_manager.add_task(event)
1801
1802        except Exception as e:
1803            self.log.exception(e)
1804        finally:
1805            return StatefulGenerationClient(
1806                self.client,
1807                new_generation_id,
1808                StateType.OBSERVATION,
1809                new_trace_id,
1810                self.task_manager,
1811            )

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

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:
2049    def span(
2050        self,
2051        *,
2052        id: typing.Optional[str] = None,
2053        name: typing.Optional[str] = None,
2054        start_time: typing.Optional[dt.datetime] = None,
2055        end_time: typing.Optional[dt.datetime] = None,
2056        metadata: typing.Optional[typing.Any] = None,
2057        input: typing.Optional[typing.Any] = None,
2058        output: typing.Optional[typing.Any] = None,
2059        level: typing.Optional[SpanLevel] = None,
2060        status_message: typing.Optional[str] = None,
2061        version: typing.Optional[str] = None,
2062        **kwargs,
2063    ) -> "StatefulSpanClient":
2064        """Create a span nested within the current observation or trace.
2065
2066        A span represents durations of units of work in a trace.
2067
2068        Args:
2069            id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
2070            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2071            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2072            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2073            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2074            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.
2075            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.
2076            input (Optional[dict]): The input to the span. Can be any JSON object.
2077            output (Optional[dict]): The output to the span. Can be any JSON object.
2078            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2079            **kwargs: Additional keyword arguments to include in the span.
2080
2081        Returns:
2082            StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations.
2083
2084        Example:
2085            ```python
2086            from langfuse import Langfuse
2087
2088            langfuse = Langfuse()
2089
2090            # Create a trace
2091            trace = langfuse.trace(name = "llm-feature")
2092
2093            # Create a span
2094            retrieval = langfuse.span(name = "retrieval")
2095            ```
2096        """
2097        span_id = id or str(uuid.uuid4())
2098        try:
2099            span_body = {
2100                "id": span_id,
2101                "name": name,
2102                "start_time": start_time or _get_timestamp(),
2103                "metadata": metadata,
2104                "input": input,
2105                "output": output,
2106                "level": level,
2107                "status_message": status_message,
2108                "version": version,
2109                "end_time": end_time,
2110                **kwargs,
2111            }
2112
2113            self.log.debug(f"Creating span {span_body}...")
2114
2115            new_dict = self._add_state_to_event(span_body)
2116            new_body = self._add_default_values(new_dict)
2117
2118            event = CreateSpanBody(**new_body)
2119
2120            event = {
2121                "id": str(uuid.uuid4()),
2122                "type": "span-create",
2123                "body": event.dict(exclude_none=True),
2124            }
2125
2126            self.task_manager.add_task(event)
2127        except Exception as e:
2128            self.log.exception(e)
2129        finally:
2130            return StatefulSpanClient(
2131                self.client,
2132                span_id,
2133                StateType.OBSERVATION,
2134                self.trace_id,
2135                task_manager=self.task_manager,
2136            )

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:
2164    def score(
2165        self,
2166        *,
2167        id: typing.Optional[str] = None,
2168        name: str,
2169        value: typing.Union[float, str],
2170        data_type: typing.Optional[ScoreDataType] = None,
2171        comment: typing.Optional[str] = None,
2172        config_id: typing.Optional[str] = None,
2173        **kwargs,
2174    ) -> "StatefulClient":
2175        """Create a score attached for the current observation or trace.
2176
2177        Args:
2178            name (str): Identifier of the score.
2179            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.
2180            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.
2181              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.
2182            comment (Optional[str]): Additional context/explanation of the score.
2183            id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
2184            config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
2185            **kwargs: Additional keyword arguments to include in the score.
2186
2187        Returns:
2188            StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.
2189
2190        Example:
2191            ```python
2192            from langfuse import Langfuse
2193
2194            langfuse = Langfuse()
2195
2196            # Create a trace
2197            trace = langfuse.trace(name="example-application")
2198
2199            # Add score to the trace
2200            trace = trace.score(
2201                name="user-explicit-feedback",
2202                value=0.8,
2203                comment="I like how personalized the response is"
2204            )
2205            ```
2206        """
2207        score_id = id or str(uuid.uuid4())
2208        try:
2209            new_score = {
2210                "id": score_id,
2211                "trace_id": self.trace_id,
2212                "name": name,
2213                "value": value,
2214                "data_type": data_type,
2215                "comment": comment,
2216                "config_id": config_id,
2217                **kwargs,
2218            }
2219
2220            self.log.debug(f"Creating score {new_score}...")
2221
2222            new_dict = self._add_state_to_event(new_score)
2223
2224            if self.state_type == StateType.OBSERVATION:
2225                new_dict["observationId"] = self.id
2226
2227            request = ScoreBody(**new_dict)
2228
2229            event = {
2230                "id": str(uuid.uuid4()),
2231                "type": "score-create",
2232                "body": request.dict(exclude_none=True),
2233            }
2234
2235            self.task_manager.add_task(event)
2236
2237        except Exception as e:
2238            self.log.exception(e)
2239        finally:
2240            return StatefulClient(
2241                self.client,
2242                self.id,
2243                self.state_type,
2244                self.trace_id,
2245                task_manager=self.task_manager,
2246            )

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:
2248    def event(
2249        self,
2250        *,
2251        id: typing.Optional[str] = None,
2252        name: typing.Optional[str] = None,
2253        start_time: typing.Optional[dt.datetime] = None,
2254        metadata: typing.Optional[typing.Any] = None,
2255        input: typing.Optional[typing.Any] = None,
2256        output: typing.Optional[typing.Any] = None,
2257        level: typing.Optional[SpanLevel] = None,
2258        status_message: typing.Optional[str] = None,
2259        version: typing.Optional[str] = None,
2260        **kwargs,
2261    ) -> "StatefulClient":
2262        """Create an event nested within the current observation or trace.
2263
2264        An event represents a discrete event in a trace.
2265
2266        Args:
2267            id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
2268            name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
2269            start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
2270            metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
2271            input (Optional[Any]): The input to the event. Can be any JSON object.
2272            output (Optional[Any]): The output to the event. Can be any JSON object.
2273            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.
2274            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.
2275            version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging.
2276            **kwargs: Additional keyword arguments to include in the event.
2277
2278        Returns:
2279            StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations.
2280
2281        Example:
2282            ```python
2283            from langfuse import Langfuse
2284
2285            langfuse = Langfuse()
2286
2287            # Create a trace
2288            trace = langfuse.trace(name = "llm-feature")
2289
2290            # Create an event
2291            retrieval = trace.event(name = "retrieval")
2292            ```
2293        """
2294        event_id = id or str(uuid.uuid4())
2295        try:
2296            event_body = {
2297                "id": event_id,
2298                "name": name,
2299                "start_time": start_time or _get_timestamp(),
2300                "metadata": metadata,
2301                "input": input,
2302                "output": output,
2303                "level": level,
2304                "status_message": status_message,
2305                "version": version,
2306                **kwargs,
2307            }
2308
2309            new_dict = self._add_state_to_event(event_body)
2310            new_body = self._add_default_values(new_dict)
2311
2312            request = CreateEventBody(**new_body)
2313
2314            event = {
2315                "id": str(uuid.uuid4()),
2316                "type": "event-create",
2317                "body": request.dict(exclude_none=True),
2318            }
2319
2320            self.log.debug(f"Creating event {event}...")
2321            self.task_manager.add_task(event)
2322
2323        except Exception as e:
2324            self.log.exception(e)
2325        finally:
2326            return StatefulClient(
2327                self.client,
2328                event_id,
2329                StateType.OBSERVATION,
2330                self.trace_id,
2331                self.task_manager,
2332            )

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

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)
2355    def __init__(
2356        self,
2357        client: FernLangfuse,
2358        id: str,
2359        state_type: StateType,
2360        trace_id: str,
2361        task_manager: TaskManager,
2362    ):
2363        """Initialize the StatefulGenerationClient."""
2364        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:
2367    def update(
2368        self,
2369        *,
2370        name: typing.Optional[str] = None,
2371        start_time: typing.Optional[dt.datetime] = None,
2372        end_time: typing.Optional[dt.datetime] = None,
2373        completion_start_time: typing.Optional[dt.datetime] = None,
2374        metadata: typing.Optional[typing.Any] = None,
2375        level: typing.Optional[SpanLevel] = None,
2376        status_message: typing.Optional[str] = None,
2377        version: typing.Optional[str] = None,
2378        model: typing.Optional[str] = None,
2379        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2380        input: typing.Optional[typing.Any] = None,
2381        output: typing.Optional[typing.Any] = None,
2382        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2383        prompt: typing.Optional[PromptClient] = None,
2384        **kwargs,
2385    ) -> "StatefulGenerationClient":
2386        """Update the generation.
2387
2388        Args:
2389            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2390            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2391            end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`.
2392            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.
2393            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2394            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.
2395            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.
2396            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2397            model (Optional[str]): The name of the model used for the generation.
2398            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2399            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2400            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2401            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.
2402            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2403            **kwargs: Additional keyword arguments to include in the generation.
2404
2405        Returns:
2406            StatefulGenerationClient: The updated generation. Passthrough for chaining.
2407
2408        Example:
2409            ```python
2410            from langfuse import Langfuse
2411
2412            langfuse = Langfuse()
2413
2414            # Create a trace
2415            trace = langfuse.trace(name = "llm-feature")
2416
2417            # Create a nested generation in Langfuse
2418            generation = trace.generation(name="summary-generation")
2419
2420            # Update the generation
2421            generation = generation.update(metadata={"interface": "whatsapp"})
2422            ```
2423        """
2424        try:
2425            generation_body = {
2426                "id": self.id,
2427                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2428                "name": name,
2429                "start_time": start_time,
2430                "metadata": metadata,
2431                "level": level,
2432                "status_message": status_message,
2433                "version": version,
2434                "end_time": end_time,
2435                "completion_start_time": completion_start_time,
2436                "model": model,
2437                "model_parameters": model_parameters,
2438                "input": input,
2439                "output": output,
2440                "usage": _convert_usage_input(usage) if usage is not None else None,
2441                **_create_prompt_context(prompt),
2442                **kwargs,
2443            }
2444
2445            self.log.debug(f"Update generation {generation_body}...")
2446
2447            request = UpdateGenerationBody(**generation_body)
2448
2449            event = {
2450                "id": str(uuid.uuid4()),
2451                "type": "generation-update",
2452                "body": request.dict(exclude_none=True, exclude_unset=False),
2453            }
2454
2455            self.log.debug(f"Update generation {event}...")
2456            self.task_manager.add_task(event)
2457
2458        except Exception as e:
2459            self.log.exception(e)
2460        finally:
2461            return StatefulGenerationClient(
2462                self.client,
2463                self.id,
2464                StateType.OBSERVATION,
2465                self.trace_id,
2466                task_manager=self.task_manager,
2467            )

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:
2469    def end(
2470        self,
2471        *,
2472        name: typing.Optional[str] = None,
2473        start_time: typing.Optional[dt.datetime] = None,
2474        end_time: typing.Optional[dt.datetime] = None,
2475        completion_start_time: typing.Optional[dt.datetime] = None,
2476        metadata: typing.Optional[typing.Any] = None,
2477        level: typing.Optional[SpanLevel] = None,
2478        status_message: typing.Optional[str] = None,
2479        version: typing.Optional[str] = None,
2480        model: typing.Optional[str] = None,
2481        model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None,
2482        input: typing.Optional[typing.Any] = None,
2483        output: typing.Optional[typing.Any] = None,
2484        usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None,
2485        prompt: typing.Optional[PromptClient] = None,
2486        **kwargs,
2487    ) -> "StatefulGenerationClient":
2488        """End the generation, optionally updating its properties.
2489
2490        Args:
2491            name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
2492            start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
2493            end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
2494            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.
2495            metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
2496            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.
2497            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.
2498            version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2499            model (Optional[str]): The name of the model used for the generation.
2500            model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
2501            input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
2502            output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
2503            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.
2504            prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
2505            **kwargs: Additional keyword arguments to include in the generation.
2506
2507        Returns:
2508            StatefulGenerationClient: The ended generation. Passthrough for chaining.
2509
2510        Example:
2511            ```python
2512            from langfuse import Langfuse
2513
2514            langfuse = Langfuse()
2515
2516            # Create a trace
2517            trace = langfuse.trace(name = "llm-feature")
2518
2519            # Create a nested generation in Langfuse
2520            generation = trace.generation(name="summary-generation")
2521
2522            # End the generation and update its properties
2523            generation = generation.end(metadata={"interface": "whatsapp"})
2524            ```
2525        """
2526        return self.update(
2527            name=name,
2528            start_time=start_time,
2529            end_time=end_time or _get_timestamp(),
2530            metadata=metadata,
2531            level=level,
2532            status_message=status_message,
2533            version=version,
2534            completion_start_time=completion_start_time,
2535            model=model,
2536            model_parameters=model_parameters,
2537            input=input,
2538            output=output,
2539            usage=usage,
2540            prompt=prompt,
2541            **kwargs,
2542        )

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

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)
2558    def __init__(
2559        self,
2560        client: FernLangfuse,
2561        id: str,
2562        state_type: StateType,
2563        trace_id: str,
2564        task_manager: TaskManager,
2565    ):
2566        """Initialize the StatefulSpanClient."""
2567        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:
2570    def update(
2571        self,
2572        *,
2573        name: typing.Optional[str] = None,
2574        start_time: typing.Optional[dt.datetime] = None,
2575        end_time: typing.Optional[dt.datetime] = None,
2576        metadata: typing.Optional[typing.Any] = None,
2577        input: typing.Optional[typing.Any] = None,
2578        output: typing.Optional[typing.Any] = None,
2579        level: typing.Optional[SpanLevel] = None,
2580        status_message: typing.Optional[str] = None,
2581        version: typing.Optional[str] = None,
2582        **kwargs,
2583    ) -> "StatefulSpanClient":
2584        """Update the span.
2585
2586        Args:
2587            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2588            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2589            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2590            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2591            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.
2592            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.
2593            input (Optional[dict]): The input to the span. Can be any JSON object.
2594            output (Optional[dict]): The output to the span. Can be any JSON object.
2595            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2596            **kwargs: Additional keyword arguments to include in the span.
2597
2598        Returns:
2599            StatefulSpanClient: The updated span. Passthrough for chaining.
2600
2601        Example:
2602            ```python
2603            from langfuse import Langfuse
2604
2605            langfuse = Langfuse()
2606
2607            # Create a trace
2608            trace = langfuse.trace(name = "llm-feature")
2609
2610            # Create a nested span in Langfuse
2611            span = trace.span(name="retrieval")
2612
2613            # Update the span
2614            span = span.update(metadata={"interface": "whatsapp"})
2615            ```
2616        """
2617        try:
2618            span_body = {
2619                "id": self.id,
2620                "trace_id": self.trace_id,  # Included to avoid relying on the order of events sent to the API
2621                "name": name,
2622                "start_time": start_time,
2623                "metadata": metadata,
2624                "input": input,
2625                "output": output,
2626                "level": level,
2627                "status_message": status_message,
2628                "version": version,
2629                "end_time": end_time,
2630                **kwargs,
2631            }
2632            self.log.debug(f"Update span {span_body}...")
2633
2634            request = UpdateSpanBody(**span_body)
2635
2636            event = {
2637                "id": str(uuid.uuid4()),
2638                "type": "span-update",
2639                "body": request.dict(exclude_none=True),
2640            }
2641
2642            self.task_manager.add_task(event)
2643        except Exception as e:
2644            self.log.exception(e)
2645        finally:
2646            return StatefulSpanClient(
2647                self.client,
2648                self.id,
2649                StateType.OBSERVATION,
2650                self.trace_id,
2651                task_manager=self.task_manager,
2652            )

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:
2654    def end(
2655        self,
2656        *,
2657        name: typing.Optional[str] = None,
2658        start_time: typing.Optional[dt.datetime] = None,
2659        end_time: typing.Optional[dt.datetime] = None,
2660        metadata: typing.Optional[typing.Any] = None,
2661        input: typing.Optional[typing.Any] = None,
2662        output: typing.Optional[typing.Any] = None,
2663        level: typing.Optional[SpanLevel] = None,
2664        status_message: typing.Optional[str] = None,
2665        version: typing.Optional[str] = None,
2666        **kwargs,
2667    ) -> "StatefulSpanClient":
2668        """End the span, optionally updating its properties.
2669
2670        Args:
2671            name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
2672            start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
2673            end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`.
2674            metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
2675            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.
2676            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.
2677            input (Optional[dict]): The input to the span. Can be any JSON object.
2678            output (Optional[dict]): The output to the span. Can be any JSON object.
2679            version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging.
2680            **kwargs: Additional keyword arguments to include in the span.
2681
2682        Returns:
2683            StatefulSpanClient: The updated span. Passthrough for chaining.
2684
2685        Example:
2686            ```python
2687            from langfuse import Langfuse
2688
2689            langfuse = Langfuse()
2690
2691            # Create a trace
2692            trace = langfuse.trace(name = "llm-feature")
2693
2694            # Create a nested span in Langfuse
2695            span = trace.span(name="retrieval")
2696
2697            # End the span and update its properties
2698            span = span.end(metadata={"interface": "whatsapp"})
2699            ```
2700        """
2701        try:
2702            span_body = {
2703                "name": name,
2704                "start_time": start_time,
2705                "metadata": metadata,
2706                "input": input,
2707                "output": output,
2708                "level": level,
2709                "status_message": status_message,
2710                "version": version,
2711                "end_time": end_time or _get_timestamp(),
2712                **kwargs,
2713            }
2714            return self.update(**span_body)
2715
2716        except Exception as e:
2717            self.log.warning(e)
2718        finally:
2719            return StatefulSpanClient(
2720                self.client,
2721                self.id,
2722                StateType.OBSERVATION,
2723                self.trace_id,
2724                task_manager=self.task_manager,
2725            )

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):
2727    def get_langchain_handler(self, update_parent: bool = False):
2728        """Get langchain callback handler associated with the current span.
2729
2730        Args:
2731            update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
2732
2733        Returns:
2734            CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
2735        """
2736        from langfuse.callback import CallbackHandler
2737
2738        return CallbackHandler(
2739            stateful_client=self, update_stateful_client=update_parent
2740        )

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):
2743class StatefulTraceClient(StatefulClient):
2744    """Class for handling stateful operations of traces in the Langfuse system. Inherits from StatefulClient.
2745
2746    Attributes:
2747        client (FernLangfuse): Core interface for Langfuse API interaction.
2748        id (str): Unique identifier of the trace.
2749        state_type (StateType): Type of the stateful entity (observation or trace).
2750        trace_id (str): The trace ID associated with this client.
2751        task_manager (TaskManager): Manager for handling asynchronous tasks.
2752    """
2753
2754    log = logging.getLogger("langfuse")
2755
2756    def __init__(
2757        self,
2758        client: FernLangfuse,
2759        id: str,
2760        state_type: StateType,
2761        trace_id: str,
2762        task_manager: TaskManager,
2763    ):
2764        """Initialize the StatefulTraceClient."""
2765        super().__init__(client, id, state_type, trace_id, task_manager)
2766        self.task_manager = task_manager
2767
2768    def update(
2769        self,
2770        *,
2771        name: typing.Optional[str] = None,
2772        user_id: typing.Optional[str] = None,
2773        session_id: typing.Optional[str] = None,
2774        version: typing.Optional[str] = None,
2775        release: typing.Optional[str] = None,
2776        input: typing.Optional[typing.Any] = None,
2777        output: typing.Optional[typing.Any] = None,
2778        metadata: typing.Optional[typing.Any] = None,
2779        tags: typing.Optional[typing.List[str]] = None,
2780        public: typing.Optional[bool] = None,
2781        **kwargs,
2782    ) -> "StatefulTraceClient":
2783        """Update the trace.
2784
2785        Args:
2786            name: Identifier of the trace. Useful for sorting/filtering in the UI.
2787            input: The input of the trace. Can be any JSON object.
2788            output: The output of the trace. Can be any JSON object.
2789            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
2790            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
2791            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
2792            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
2793            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
2794            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.
2795            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.
2796            **kwargs: Additional keyword arguments that can be included in the trace.
2797
2798        Returns:
2799            StatefulTraceClient: The updated trace. Passthrough for chaining.
2800
2801        Example:
2802            ```python
2803            from langfuse import Langfuse
2804
2805            langfuse = Langfuse()
2806
2807            # Create a trace
2808            trace = langfuse.trace(
2809                name="example-application",
2810                user_id="user-1234")
2811            )
2812
2813            # Update the trace
2814            trace = trace.update(
2815                output={"result": "success"},
2816                metadata={"interface": "whatsapp"}
2817            )
2818            ```
2819        """
2820        try:
2821            trace_body = {
2822                "id": self.id,
2823                "name": name,
2824                "userId": user_id,
2825                "sessionId": session_id
2826                or kwargs.get("sessionId", None),  # backward compatibility
2827                "version": version,
2828                "release": release,
2829                "input": input,
2830                "output": output,
2831                "metadata": metadata,
2832                "public": public,
2833                "tags": tags,
2834                **kwargs,
2835            }
2836            self.log.debug(f"Update trace {trace_body}...")
2837
2838            request = TraceBody(**trace_body)
2839
2840            event = {
2841                "id": str(uuid.uuid4()),
2842                "type": "trace-create",
2843                "body": request.dict(exclude_none=True),
2844            }
2845
2846            self.task_manager.add_task(event)
2847
2848        except Exception as e:
2849            self.log.exception(e)
2850        finally:
2851            return StatefulTraceClient(
2852                self.client,
2853                self.id,
2854                StateType.TRACE,
2855                self.trace_id,
2856                task_manager=self.task_manager,
2857            )
2858
2859    def get_langchain_handler(self, update_parent: bool = False):
2860        """Get langchain callback handler associated with the current trace.
2861
2862        This method creates and returns a CallbackHandler instance, linking it with the current
2863        trace. Use this if you want to group multiple Langchain runs within a single trace.
2864
2865        Args:
2866            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
2867
2868        Raises:
2869            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
2870
2871        Returns:
2872            CallbackHandler: Langchain callback handler linked to the current trace.
2873
2874        Example:
2875            ```python
2876            from langfuse import Langfuse
2877
2878            langfuse = Langfuse()
2879
2880            # Create a trace
2881            trace = langfuse.trace(name = "llm-feature")
2882
2883            # Get a langchain callback handler
2884            handler = trace.get_langchain_handler()
2885            ```
2886        """
2887        try:
2888            from langfuse.callback import CallbackHandler
2889
2890            self.log.debug(f"Creating new handler for trace {self.id}")
2891
2892            return CallbackHandler(
2893                stateful_client=self,
2894                debug=self.log.level == logging.DEBUG,
2895                update_stateful_client=update_parent,
2896            )
2897        except Exception as e:
2898            self.log.exception(e)
2899
2900    def getNewHandler(self):
2901        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
2902        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)
2756    def __init__(
2757        self,
2758        client: FernLangfuse,
2759        id: str,
2760        state_type: StateType,
2761        trace_id: str,
2762        task_manager: TaskManager,
2763    ):
2764        """Initialize the StatefulTraceClient."""
2765        super().__init__(client, id, state_type, trace_id, task_manager)
2766        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:
2768    def update(
2769        self,
2770        *,
2771        name: typing.Optional[str] = None,
2772        user_id: typing.Optional[str] = None,
2773        session_id: typing.Optional[str] = None,
2774        version: typing.Optional[str] = None,
2775        release: typing.Optional[str] = None,
2776        input: typing.Optional[typing.Any] = None,
2777        output: typing.Optional[typing.Any] = None,
2778        metadata: typing.Optional[typing.Any] = None,
2779        tags: typing.Optional[typing.List[str]] = None,
2780        public: typing.Optional[bool] = None,
2781        **kwargs,
2782    ) -> "StatefulTraceClient":
2783        """Update the trace.
2784
2785        Args:
2786            name: Identifier of the trace. Useful for sorting/filtering in the UI.
2787            input: The input of the trace. Can be any JSON object.
2788            output: The output of the trace. Can be any JSON object.
2789            metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
2790            user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
2791            session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
2792            version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
2793            release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
2794            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.
2795            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.
2796            **kwargs: Additional keyword arguments that can be included in the trace.
2797
2798        Returns:
2799            StatefulTraceClient: The updated trace. Passthrough for chaining.
2800
2801        Example:
2802            ```python
2803            from langfuse import Langfuse
2804
2805            langfuse = Langfuse()
2806
2807            # Create a trace
2808            trace = langfuse.trace(
2809                name="example-application",
2810                user_id="user-1234")
2811            )
2812
2813            # Update the trace
2814            trace = trace.update(
2815                output={"result": "success"},
2816                metadata={"interface": "whatsapp"}
2817            )
2818            ```
2819        """
2820        try:
2821            trace_body = {
2822                "id": self.id,
2823                "name": name,
2824                "userId": user_id,
2825                "sessionId": session_id
2826                or kwargs.get("sessionId", None),  # backward compatibility
2827                "version": version,
2828                "release": release,
2829                "input": input,
2830                "output": output,
2831                "metadata": metadata,
2832                "public": public,
2833                "tags": tags,
2834                **kwargs,
2835            }
2836            self.log.debug(f"Update trace {trace_body}...")
2837
2838            request = TraceBody(**trace_body)
2839
2840            event = {
2841                "id": str(uuid.uuid4()),
2842                "type": "trace-create",
2843                "body": request.dict(exclude_none=True),
2844            }
2845
2846            self.task_manager.add_task(event)
2847
2848        except Exception as e:
2849            self.log.exception(e)
2850        finally:
2851            return StatefulTraceClient(
2852                self.client,
2853                self.id,
2854                StateType.TRACE,
2855                self.trace_id,
2856                task_manager=self.task_manager,
2857            )

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):
2859    def get_langchain_handler(self, update_parent: bool = False):
2860        """Get langchain callback handler associated with the current trace.
2861
2862        This method creates and returns a CallbackHandler instance, linking it with the current
2863        trace. Use this if you want to group multiple Langchain runs within a single trace.
2864
2865        Args:
2866            update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
2867
2868        Raises:
2869            ImportError: If the 'langchain' module is not installed, indicating missing functionality.
2870
2871        Returns:
2872            CallbackHandler: Langchain callback handler linked to the current trace.
2873
2874        Example:
2875            ```python
2876            from langfuse import Langfuse
2877
2878            langfuse = Langfuse()
2879
2880            # Create a trace
2881            trace = langfuse.trace(name = "llm-feature")
2882
2883            # Get a langchain callback handler
2884            handler = trace.get_langchain_handler()
2885            ```
2886        """
2887        try:
2888            from langfuse.callback import CallbackHandler
2889
2890            self.log.debug(f"Creating new handler for trace {self.id}")
2891
2892            return CallbackHandler(
2893                stateful_client=self,
2894                debug=self.log.level == logging.DEBUG,
2895                update_stateful_client=update_parent,
2896            )
2897        except Exception as e:
2898            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):
2900    def getNewHandler(self):
2901        """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated."""
2902        return self.get_langchain_handler()

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

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

Initialize the DatasetItemClient.

log = <Logger langfuse (WARNING)>
id: str
status: langfuse.api.resources.commons.types.dataset_status.DatasetStatus
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):
2974    def flush(self, observation: StatefulClient, run_name: str):
2975        """Flushes an observations task manager's queue.
2976
2977        Used before creating a dataset run item to ensure all events are persistent.
2978
2979        Args:
2980            observation (StatefulClient): The observation or trace client associated with the dataset item.
2981            run_name (str): The name of the dataset run.
2982        """
2983        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):
3046    def get_langchain_handler(
3047        self,
3048        *,
3049        run_name: str,
3050        run_description: Optional[str] = None,
3051        run_metadata: Optional[Any] = None,
3052    ):
3053        """Create and get a langchain callback handler linked to this dataset item.
3054
3055        Args:
3056            run_name (str): The name of the dataset run to be used in the callback handler.
3057            run_description (Optional[str]): Description of the dataset run.
3058            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3059
3060        Returns:
3061            CallbackHandler: An instance of CallbackHandler linked to the dataset item.
3062        """
3063        metadata = {
3064            "dataset_item_id": self.id,
3065            "run_name": run_name,
3066            "dataset_id": self.dataset_id,
3067        }
3068        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3069
3070        self.link(
3071            trace, run_name, run_metadata=run_metadata, run_description=run_description
3072        )
3073
3074        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):
3076    @contextmanager
3077    def observe(
3078        self,
3079        *,
3080        run_name: str,
3081        run_description: Optional[str] = None,
3082        run_metadata: Optional[Any] = None,
3083        trace_id: Optional[str] = None,
3084    ):
3085        """Observes a dataset run within the Langfuse client.
3086
3087        Args:
3088            run_name (str): The name of the dataset run.
3089            root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created.
3090            run_description (Optional[str]): The description of the dataset run.
3091            run_metadata (Optional[Any]): Additional metadata for the dataset run.
3092
3093        Yields:
3094            StatefulTraceClient: The trace associated with the dataset run.
3095        """
3096        from langfuse.decorators import langfuse_context
3097
3098        root_trace_id = trace_id or str(uuid.uuid4())
3099
3100        langfuse_context._set_root_trace_id(root_trace_id)
3101
3102        try:
3103            yield root_trace_id
3104
3105        finally:
3106            self.link(
3107                run_name=run_name,
3108                run_metadata=run_metadata,
3109                run_description=run_description,
3110                trace_or_observation=None,
3111                trace_id=root_trace_id,
3112            )

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]] = {}):
3114    @contextmanager
3115    def observe_llama_index(
3116        self,
3117        *,
3118        run_name: str,
3119        run_description: Optional[str] = None,
3120        run_metadata: Optional[Any] = None,
3121        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3122    ):
3123        """Context manager for observing LlamaIndex operations linked to this dataset item.
3124
3125        This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging
3126        and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all
3127        operations performed within the context are linked to the appropriate dataset item and run in Langfuse.
3128
3129        Args:
3130            run_name (str): The name of the dataset run.
3131            run_description (Optional[str]): Description of the dataset run. Defaults to None.
3132            run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
3133            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass
3134                to the LlamaIndex integration constructor. Defaults to an empty dictionary.
3135
3136        Yields:
3137            LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.
3138
3139        Example:
3140            ```python
3141            dataset_item = dataset.items[0]
3142
3143            with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler:
3144                # Perform LlamaIndex operations here
3145                some_llama_index_operation()
3146            ```
3147
3148        Raises:
3149            ImportError: If required modules for LlamaIndex integration are not available.
3150        """
3151        metadata = {
3152            "dataset_item_id": self.id,
3153            "run_name": run_name,
3154            "dataset_id": self.dataset_id,
3155        }
3156        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3157        self.link(
3158            trace, run_name, run_metadata=run_metadata, run_description=run_description
3159        )
3160
3161        try:
3162            import llama_index.core
3163            from llama_index.core import Settings
3164            from llama_index.core.callbacks import CallbackManager
3165
3166            from langfuse.llama_index import LlamaIndexCallbackHandler
3167
3168            callback_handler = LlamaIndexCallbackHandler(
3169                **llama_index_integration_constructor_kwargs,
3170            )
3171            callback_handler.set_root(trace, update_root=True)
3172
3173            # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler
3174            # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it
3175            prev_global_handler = llama_index.core.global_handler
3176            prev_langfuse_handler = None
3177
3178            if isinstance(prev_global_handler, LlamaIndexCallbackHandler):
3179                llama_index.core.global_handler = None
3180
3181            if Settings.callback_manager is None:
3182                Settings.callback_manager = CallbackManager([callback_handler])
3183            else:
3184                for handler in Settings.callback_manager.handlers:
3185                    if isinstance(handler, LlamaIndexCallbackHandler):
3186                        prev_langfuse_handler = handler
3187                        Settings.callback_manager.remove_handler(handler)
3188
3189                Settings.callback_manager.add_handler(callback_handler)
3190
3191        except Exception as e:
3192            self.log.exception(e)
3193
3194        try:
3195            yield callback_handler
3196        finally:
3197            # Reset the handlers
3198            Settings.callback_manager.remove_handler(callback_handler)
3199            if prev_langfuse_handler is not None:
3200                Settings.callback_manager.add_handler(prev_langfuse_handler)
3201
3202            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]] = {}):
3204    def get_llama_index_handler(
3205        self,
3206        *,
3207        run_name: str,
3208        run_description: Optional[str] = None,
3209        run_metadata: Optional[Any] = None,
3210        llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {},
3211    ):
3212        """Create and get a llama-index callback handler linked to this dataset item.
3213
3214        Args:
3215            run_name (str): The name of the dataset run to be used in the callback handler.
3216            run_description (Optional[str]): Description of the dataset run.
3217            run_metadata (Optional[Any]): Additional metadata to include in dataset run.
3218            llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
3219
3220        Returns:
3221            LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.
3222        """
3223        metadata = {
3224            "dataset_item_id": self.id,
3225            "run_name": run_name,
3226            "dataset_id": self.dataset_id,
3227        }
3228        trace = self.langfuse.trace(name="dataset-run", metadata=metadata)
3229
3230        self.link(
3231            trace, run_name, run_metadata=run_metadata, run_description=run_description
3232        )
3233
3234        try:
3235            from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler
3236
3237            callback_handler = LlamaIndexCallbackHandler(
3238                **llama_index_integration_constructor_kwargs,
3239            )
3240            callback_handler.set_root(trace, update_root=True)
3241
3242            return callback_handler
3243        except Exception as e:
3244            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:
3247class DatasetClient:
3248    """Class for managing datasets in Langfuse.
3249
3250    Attributes:
3251        id (str): Unique identifier of the dataset.
3252        name (str): Name of the dataset.
3253        description (Optional[str]): Description of the dataset.
3254        metadata (Optional[typing.Any]): Additional metadata of the dataset.
3255        project_id (str): Identifier of the project to which the dataset belongs.
3256        dataset_name (str): Name of the dataset.
3257        created_at (datetime): Timestamp of dataset creation.
3258        updated_at (datetime): Timestamp of the last update to the dataset.
3259        items (List[DatasetItemClient]): List of dataset items associated with the dataset.
3260        runs (List[str]): List of dataset runs associated with the dataset. Deprecated.
3261
3262    Example:
3263        Print the input of each dataset item in a dataset.
3264        ```python
3265        from langfuse import Langfuse
3266
3267        langfuse = Langfuse()
3268
3269        dataset = langfuse.get_dataset("<dataset_name>")
3270
3271        for item in dataset.items:
3272            print(item.input)
3273        ```
3274    """
3275
3276    id: str
3277    name: str
3278    description: Optional[str]
3279    project_id: str
3280    dataset_name: str  # for backward compatibility, to be deprecated
3281    metadata: Optional[Any]
3282    created_at: dt.datetime
3283    updated_at: dt.datetime
3284    items: typing.List[DatasetItemClient]
3285    runs: typing.List[str] = []  # deprecated
3286
3287    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3288        """Initialize the DatasetClient."""
3289        self.id = dataset.id
3290        self.name = dataset.name
3291        self.description = dataset.description
3292        self.project_id = dataset.project_id
3293        self.metadata = dataset.metadata
3294        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3295        self.created_at = dataset.created_at
3296        self.updated_at = dataset.updated_at
3297        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.resources.commons.types.dataset.Dataset, items: List[DatasetItemClient])
3287    def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]):
3288        """Initialize the DatasetClient."""
3289        self.id = dataset.id
3290        self.name = dataset.name
3291        self.description = dataset.description
3292        self.project_id = dataset.project_id
3293        self.metadata = dataset.metadata
3294        self.dataset_name = dataset.name  # for backward compatibility, to be deprecated
3295        self.created_at = dataset.created_at
3296        self.updated_at = dataset.updated_at
3297        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] = []