langfuse.client
1import datetime as dt 2import logging 3import os 4import time 5import tracemalloc 6import typing 7import urllib.parse 8import uuid 9import warnings 10from contextlib import contextmanager 11from dataclasses import dataclass 12from enum import Enum 13from typing import Any, Dict, List, Literal, Optional, Sequence, Union, overload 14 15import backoff 16import httpx 17 18from langfuse.api.resources.commons.types.dataset_run_with_items import ( 19 DatasetRunWithItems, 20) 21from langfuse.api.resources.commons.types.observations_view import ObservationsView 22from langfuse.api.resources.commons.types.session import Session 23from langfuse.api.resources.commons.types.trace_with_details import TraceWithDetails 24from langfuse.api.resources.datasets.types.paginated_dataset_runs import ( 25 PaginatedDatasetRuns, 26) 27from langfuse.api.resources.ingestion.types.create_event_body import CreateEventBody 28from langfuse.api.resources.ingestion.types.create_generation_body import ( 29 CreateGenerationBody, 30) 31from langfuse.api.resources.ingestion.types.create_span_body import CreateSpanBody 32from langfuse.api.resources.ingestion.types.score_body import ScoreBody 33from langfuse.api.resources.ingestion.types.sdk_log_body import SdkLogBody 34from langfuse.api.resources.ingestion.types.trace_body import TraceBody 35from langfuse.api.resources.ingestion.types.update_generation_body import ( 36 UpdateGenerationBody, 37) 38from langfuse.api.resources.ingestion.types.update_span_body import UpdateSpanBody 39from langfuse.api.resources.media import GetMediaResponse 40from langfuse.api.resources.observations.types.observations_views import ( 41 ObservationsViews, 42) 43from langfuse.api.resources.prompts.types import ( 44 CreatePromptRequest_Chat, 45 CreatePromptRequest_Text, 46 Prompt_Chat, 47 Prompt_Text, 48) 49from langfuse.api.resources.trace.types.traces import Traces 50from langfuse.api.resources.utils.resources.pagination.types.meta_response import ( 51 MetaResponse, 52) 53from langfuse.model import ( 54 ChatMessageDict, 55 ChatPromptClient, 56 CreateDatasetItemRequest, 57 CreateDatasetRequest, 58 CreateDatasetRunItemRequest, 59 DatasetItem, 60 DatasetStatus, 61 ModelUsage, 62 PromptClient, 63 TextPromptClient, 64) 65from langfuse.parse_error import handle_fern_exception 66from langfuse.prompt_cache import PromptCache 67 68try: 69 import pydantic.v1 as pydantic # type: ignore 70except ImportError: 71 import pydantic # type: ignore 72 73from langfuse._task_manager.task_manager import TaskManager 74from langfuse.api.client import AsyncFernLangfuse, FernLangfuse 75from langfuse.environment import get_common_release_envs 76from langfuse.logging import clean_logger 77from langfuse.media import LangfuseMedia 78from langfuse.model import Dataset, MapValue, Observation, TraceWithFullDetails 79from langfuse.request import LangfuseClient 80from langfuse.types import MaskFunction, ScoreDataType, SpanLevel 81from langfuse.utils import ( 82 _convert_usage_input, 83 _create_prompt_context, 84 _get_timestamp, 85) 86 87from .version import __version__ as version 88 89 90@dataclass 91class FetchTracesResponse: 92 """Response object for fetch_traces method.""" 93 94 data: typing.List[TraceWithDetails] 95 meta: MetaResponse 96 97 98@dataclass 99class FetchTraceResponse: 100 """Response object for fetch_trace method.""" 101 102 data: TraceWithFullDetails 103 104 105@dataclass 106class FetchObservationsResponse: 107 """Response object for fetch_observations method.""" 108 109 data: typing.List[ObservationsView] 110 meta: MetaResponse 111 112 113@dataclass 114class FetchObservationResponse: 115 """Response object for fetch_observation method.""" 116 117 data: Observation 118 119 120@dataclass 121class FetchMediaResponse: 122 """Response object for fetch_media method.""" 123 124 data: GetMediaResponse 125 126 127@dataclass 128class FetchSessionsResponse: 129 """Response object for fetch_sessions method.""" 130 131 data: typing.List[Session] 132 meta: MetaResponse 133 134 135class Langfuse(object): 136 """Langfuse Python client. 137 138 Attributes: 139 log (logging.Logger): Logger for the Langfuse client. 140 base_url (str): Base URL of the Langfuse API, serving as the root address for API endpoint construction. 141 httpx_client (httpx.Client): HTTPX client utilized for executing requests to the Langfuse API. 142 client (FernLangfuse): Core interface for Langfuse API interaction. 143 task_manager (TaskManager): Task Manager dedicated to handling asynchronous tasks. 144 release (str): Identifies the release number or hash of the application. 145 prompt_cache (PromptCache): A cache for efficiently storing and retrieving PromptClient instances. 146 147 Example: 148 Initiating the Langfuse client should always be first step to use Langfuse. 149 ```python 150 import os 151 from langfuse import Langfuse 152 153 # Set the public and secret keys as environment variables 154 os.environ['LANGFUSE_PUBLIC_KEY'] = public_key 155 os.environ['LANGFUSE_SECRET_KEY'] = secret_key 156 157 # Initialize the Langfuse client using the credentials 158 langfuse = Langfuse() 159 ``` 160 """ 161 162 log = logging.getLogger("langfuse") 163 """Logger for the Langfuse client.""" 164 165 host: str 166 """Host of Langfuse API.""" 167 168 project_id: Optional[str] 169 """Project ID of the Langfuse project associated with the API keys provided.""" 170 171 def __init__( 172 self, 173 public_key: Optional[str] = None, 174 secret_key: Optional[str] = None, 175 host: Optional[str] = None, 176 release: Optional[str] = None, 177 debug: bool = False, 178 threads: Optional[int] = None, 179 flush_at: Optional[int] = None, 180 flush_interval: Optional[float] = None, 181 max_retries: Optional[int] = None, 182 timeout: Optional[int] = None, # seconds 183 sdk_integration: Optional[str] = "default", 184 httpx_client: Optional[httpx.Client] = None, 185 enabled: Optional[bool] = True, 186 sample_rate: Optional[float] = None, 187 mask: Optional[MaskFunction] = None, 188 ): 189 """Initialize the Langfuse client. 190 191 Args: 192 public_key: Public API key of Langfuse project. Can be set via `LANGFUSE_PUBLIC_KEY` environment variable. 193 secret_key: Secret API key of Langfuse project. Can be set via `LANGFUSE_SECRET_KEY` environment variable. 194 host: Host of Langfuse API. Can be set via `LANGFUSE_HOST` environment variable. Defaults to `https://cloud.langfuse.com`. 195 release: Release number/hash of the application to provide analytics grouped by release. Can be set via `LANGFUSE_RELEASE` environment variable. 196 debug: Enables debug mode for more verbose logging. Can be set via `LANGFUSE_DEBUG` environment variable. 197 threads: Number of consumer threads to execute network requests. Helps scaling the SDK for high load. Only increase this if you run into scaling issues. 198 flush_at: Max batch size that's sent to the API. 199 flush_interval: Max delay until a new batch is sent to the API. 200 max_retries: Max number of retries in case of API/network errors. 201 timeout: Timeout of API requests in seconds. Defaults to 20 seconds. 202 httpx_client: Pass your own httpx client for more customizability of requests. 203 sdk_integration: Used by intgerations that wrap the Langfuse SDK to add context for debugging and support. Not to be used directly. 204 enabled: Enables or disables the Langfuse client. If disabled, all observability calls to the backend will be no-ops. 205 sample_rate: Sampling rate for tracing. If set to 0.2, only 20% of the data will be sent to the backend. Can be set via `LANGFUSE_SAMPLE_RATE` environment variable. 206 mask (langfuse.types.MaskFunction): Masking function for 'input' and 'output' fields in events. Function must take a single keyword argument `data` and return a serializable, masked version of the data. 207 208 Raises: 209 ValueError: If public_key or secret_key are not set and not found in environment variables. 210 211 Example: 212 Initiating the Langfuse client should always be first step to use Langfuse. 213 ```python 214 import os 215 from langfuse import Langfuse 216 217 # Set the public and secret keys as environment variables 218 os.environ['LANGFUSE_PUBLIC_KEY'] = public_key 219 os.environ['LANGFUSE_SECRET_KEY'] = secret_key 220 221 # Initialize the Langfuse client using the credentials 222 langfuse = Langfuse() 223 ``` 224 """ 225 self.enabled = enabled 226 public_key = public_key or os.environ.get("LANGFUSE_PUBLIC_KEY") 227 secret_key = secret_key or os.environ.get("LANGFUSE_SECRET_KEY") 228 sample_rate = ( 229 sample_rate 230 if sample_rate 231 is not None # needs explicit None check, as 0 is a valid value 232 else float(os.environ.get("LANGFUSE_SAMPLE_RATE", 1.0)) 233 ) 234 235 if sample_rate is not None and ( 236 sample_rate > 1 or sample_rate < 0 237 ): # default value 1 will be set in the taskmanager 238 self.enabled = False 239 self.log.warning( 240 "Langfuse client is disabled since the sample rate provided is not between 0 and 1." 241 ) 242 243 threads = threads or int(os.environ.get("LANGFUSE_THREADS", 1)) 244 flush_at = flush_at or int(os.environ.get("LANGFUSE_FLUSH_AT", 15)) 245 flush_interval = flush_interval or float( 246 os.environ.get("LANGFUSE_FLUSH_INTERVAL", 0.5) 247 ) 248 249 max_retries = max_retries or int(os.environ.get("LANGFUSE_MAX_RETRIES", 3)) 250 timeout = timeout or int(os.environ.get("LANGFUSE_TIMEOUT", 20)) 251 252 if not self.enabled: 253 self.log.warning( 254 "Langfuse client is disabled. No observability data will be sent." 255 ) 256 257 elif not public_key: 258 self.enabled = False 259 self.log.warning( 260 "Langfuse client is disabled since no public_key was provided as a parameter or environment variable 'LANGFUSE_PUBLIC_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client" 261 ) 262 263 elif not secret_key: 264 self.enabled = False 265 self.log.warning( 266 "Langfuse client is disabled since no secret_key was provided as a parameter or environment variable 'LANGFUSE_SECRET_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client" 267 ) 268 269 set_debug = debug if debug else (os.getenv("LANGFUSE_DEBUG", "False") == "True") 270 271 if set_debug is True: 272 # Ensures that debug level messages are logged when debug mode is on. 273 # Otherwise, defaults to WARNING level. 274 # See https://docs.python.org/3/howto/logging.html#what-happens-if-no-configuration-is-provided 275 logging.basicConfig() 276 # Set level for all loggers under langfuse package 277 logging.getLogger("langfuse").setLevel(logging.DEBUG) 278 279 clean_logger() 280 else: 281 logging.getLogger("langfuse").setLevel(logging.WARNING) 282 clean_logger() 283 284 self.base_url = ( 285 host 286 if host 287 else os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com") 288 ) 289 290 self.httpx_client = httpx_client or httpx.Client(timeout=timeout) 291 292 public_api_client = FernLangfuse( 293 base_url=self.base_url, 294 username=public_key, 295 password=secret_key, 296 x_langfuse_sdk_name="python", 297 x_langfuse_sdk_version=version, 298 x_langfuse_public_key=public_key, 299 httpx_client=self.httpx_client, 300 timeout=timeout, 301 ) 302 async_public_api_client = AsyncFernLangfuse( 303 base_url=self.base_url, 304 username=public_key, 305 password=secret_key, 306 x_langfuse_sdk_name="python", 307 x_langfuse_sdk_version=version, 308 x_langfuse_public_key=public_key, 309 timeout=timeout, 310 ) 311 312 self.api = public_api_client 313 self.client = public_api_client # legacy, to be removed in next major release 314 self.async_api = async_public_api_client 315 316 langfuse_client = LangfuseClient( 317 public_key=public_key, 318 secret_key=secret_key, 319 base_url=self.base_url, 320 version=version, 321 timeout=timeout, 322 session=self.httpx_client, 323 ) 324 325 args = { 326 "threads": threads, 327 "flush_at": flush_at, 328 "flush_interval": flush_interval, 329 "max_retries": max_retries, 330 "client": langfuse_client, 331 "api_client": self.client, 332 "public_key": public_key, 333 "sdk_name": "python", 334 "sdk_version": version, 335 "sdk_integration": sdk_integration, 336 "enabled": self.enabled, 337 "sample_rate": sample_rate, 338 "mask": mask, 339 } 340 341 self.task_manager = TaskManager(**args) 342 343 self.trace_id = None 344 self.project_id = None 345 346 self.release = self._get_release_value(release) 347 348 self.prompt_cache = PromptCache() 349 350 def _get_release_value(self, release: Optional[str] = None) -> Optional[str]: 351 if release: 352 return release 353 elif "LANGFUSE_RELEASE" in os.environ: 354 return os.environ["LANGFUSE_RELEASE"] 355 else: 356 return get_common_release_envs() 357 358 def _get_project_id(self) -> Optional[str]: 359 """Fetch and return the current project id. Persisted across requests. Returns None if no project id is found for api keys.""" 360 if not self.project_id: 361 proj = self.client.projects.get() 362 if not proj.data or not proj.data[0].id: 363 return None 364 365 self.project_id = proj.data[0].id 366 367 return self.project_id 368 369 def get_trace_id(self) -> str: 370 """Get the current trace id.""" 371 return self.trace_id 372 373 def get_trace_url(self) -> str: 374 """Get the URL of the current trace to view it in the Langfuse UI.""" 375 project_id = self._get_project_id() 376 if not project_id: 377 return f"{self.base_url}/trace/{self.trace_id}" 378 379 return f"{self.base_url}/project/{project_id}/traces/{self.trace_id}" 380 381 def get_dataset( 382 self, name: str, *, fetch_items_page_size: Optional[int] = 50 383 ) -> "DatasetClient": 384 """Fetch a dataset by its name. 385 386 Args: 387 name (str): The name of the dataset to fetch. 388 fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50. 389 390 Returns: 391 DatasetClient: The dataset with the given name. 392 """ 393 try: 394 self.log.debug(f"Getting datasets {name}") 395 dataset = self.client.datasets.get(dataset_name=name) 396 397 dataset_items = [] 398 page = 1 399 while True: 400 new_items = self.client.dataset_items.list( 401 dataset_name=self._url_encode(name), 402 page=page, 403 limit=fetch_items_page_size, 404 ) 405 dataset_items.extend(new_items.data) 406 if new_items.meta.total_pages <= page: 407 break 408 page += 1 409 410 items = [DatasetItemClient(i, langfuse=self) for i in dataset_items] 411 412 return DatasetClient(dataset, items=items) 413 except Exception as e: 414 handle_fern_exception(e) 415 raise e 416 417 def get_dataset_item(self, id: str) -> "DatasetItemClient": 418 """Get the dataset item with the given id.""" 419 try: 420 self.log.debug(f"Getting dataset item {id}") 421 dataset_item = self.client.dataset_items.get(id=id) 422 return DatasetItemClient(dataset_item, langfuse=self) 423 except Exception as e: 424 handle_fern_exception(e) 425 raise e 426 427 def auth_check(self) -> bool: 428 """Check if the provided credentials (public and secret key) are valid. 429 430 Raises: 431 Exception: If no projects were found for the provided credentials. 432 433 Note: 434 This method is blocking. It is discouraged to use it in production code. 435 """ 436 try: 437 projects = self.client.projects.get() 438 self.log.debug( 439 f"Auth check successful, found {len(projects.data)} projects" 440 ) 441 if len(projects.data) == 0: 442 raise Exception( 443 "Auth check failed, no project found for the keys provided." 444 ) 445 return True 446 447 except Exception as e: 448 handle_fern_exception(e) 449 raise e 450 451 def get_dataset_runs( 452 self, 453 dataset_name: str, 454 *, 455 page: typing.Optional[int] = None, 456 limit: typing.Optional[int] = None, 457 ) -> PaginatedDatasetRuns: 458 """Get all dataset runs. 459 460 Args: 461 dataset_name (str): Name of the dataset. 462 page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None. 463 limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50. 464 465 Returns: 466 PaginatedDatasetRuns: The dataset runs. 467 """ 468 try: 469 self.log.debug("Getting dataset runs") 470 return self.client.datasets.get_runs( 471 dataset_name=self._url_encode(dataset_name), page=page, limit=limit 472 ) 473 except Exception as e: 474 handle_fern_exception(e) 475 raise e 476 477 def get_dataset_run( 478 self, 479 dataset_name: str, 480 dataset_run_name: str, 481 ) -> DatasetRunWithItems: 482 """Get a dataset run. 483 484 Args: 485 dataset_name: Name of the dataset. 486 dataset_run_name: Name of the dataset run. 487 488 Returns: 489 DatasetRunWithItems: The dataset run. 490 """ 491 try: 492 self.log.debug( 493 f"Getting dataset runs for dataset {dataset_name} and run {dataset_run_name}" 494 ) 495 return self.client.datasets.get_run( 496 dataset_name=self._url_encode(dataset_name), 497 run_name=self._url_encode(dataset_run_name), 498 ) 499 except Exception as e: 500 handle_fern_exception(e) 501 raise e 502 503 def create_dataset( 504 self, 505 name: str, 506 description: Optional[str] = None, 507 metadata: Optional[Any] = None, 508 ) -> Dataset: 509 """Create a dataset with the given name on Langfuse. 510 511 Args: 512 name: Name of the dataset to create. 513 description: Description of the dataset. Defaults to None. 514 metadata: Additional metadata. Defaults to None. 515 516 Returns: 517 Dataset: The created dataset as returned by the Langfuse API. 518 """ 519 try: 520 body = CreateDatasetRequest( 521 name=name, description=description, metadata=metadata 522 ) 523 self.log.debug(f"Creating datasets {body}") 524 return self.client.datasets.create(request=body) 525 except Exception as e: 526 handle_fern_exception(e) 527 raise e 528 529 def create_dataset_item( 530 self, 531 dataset_name: str, 532 input: Optional[Any] = None, 533 expected_output: Optional[Any] = None, 534 metadata: Optional[Any] = None, 535 source_trace_id: Optional[str] = None, 536 source_observation_id: Optional[str] = None, 537 status: Optional[DatasetStatus] = None, 538 id: Optional[str] = None, 539 ) -> DatasetItem: 540 """Create a dataset item. 541 542 Upserts if an item with id already exists. 543 544 Args: 545 dataset_name: Name of the dataset in which the dataset item should be created. 546 input: Input data. Defaults to None. Can contain any dict, list or scalar. 547 expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar. 548 metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar. 549 source_trace_id: Id of the source trace. Defaults to None. 550 source_observation_id: Id of the source observation. Defaults to None. 551 status: Status of the dataset item. Defaults to ACTIVE for newly created items. 552 id: Id of the dataset item. Defaults to None. Provide your own id if you want to dedupe dataset items. Id needs to be globally unique and cannot be reused across datasets. 553 554 Returns: 555 DatasetItem: The created dataset item as returned by the Langfuse API. 556 557 Example: 558 ```python 559 from langfuse import Langfuse 560 561 langfuse = Langfuse() 562 563 # Uploading items to the Langfuse dataset named "capital_cities" 564 langfuse.create_dataset_item( 565 dataset_name="capital_cities", 566 input={"input": {"country": "Italy"}}, 567 expected_output={"expected_output": "Rome"}, 568 metadata={"foo": "bar"} 569 ) 570 ``` 571 """ 572 try: 573 body = CreateDatasetItemRequest( 574 datasetName=dataset_name, 575 input=input, 576 expectedOutput=expected_output, 577 metadata=metadata, 578 sourceTraceId=source_trace_id, 579 sourceObservationId=source_observation_id, 580 status=status, 581 id=id, 582 ) 583 self.log.debug(f"Creating dataset item {body}") 584 return self.client.dataset_items.create(request=body) 585 except Exception as e: 586 handle_fern_exception(e) 587 raise e 588 589 def fetch_trace( 590 self, 591 id: str, 592 ) -> FetchTraceResponse: 593 """Fetch a trace via the Langfuse API by its id. 594 595 Args: 596 id: The id of the trace to fetch. 597 598 Returns: 599 FetchTraceResponse: The trace with full details as returned by the Langfuse API on `data`. 600 601 Raises: 602 Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request. 603 """ 604 try: 605 self.log.debug(f"Getting trace {id}") 606 trace = self.client.trace.get(id) 607 return FetchTraceResponse(data=trace) 608 except Exception as e: 609 handle_fern_exception(e) 610 raise e 611 612 def get_trace( 613 self, 614 id: str, 615 ) -> TraceWithFullDetails: 616 """Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead. 617 618 Args: 619 id: The id of the trace to fetch. 620 621 Returns: 622 TraceWithFullDetails: The trace with full details as returned by the Langfuse API. 623 624 Raises: 625 Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request. 626 """ 627 warnings.warn( 628 "get_trace is deprecated, use fetch_trace instead.", 629 DeprecationWarning, 630 ) 631 632 try: 633 self.log.debug(f"Getting trace {id}") 634 return self.client.trace.get(id) 635 except Exception as e: 636 handle_fern_exception(e) 637 raise e 638 639 def fetch_traces( 640 self, 641 *, 642 page: Optional[int] = None, 643 limit: Optional[int] = None, 644 user_id: Optional[str] = None, 645 name: Optional[str] = None, 646 session_id: Optional[str] = None, 647 from_timestamp: Optional[dt.datetime] = None, 648 to_timestamp: Optional[dt.datetime] = None, 649 order_by: Optional[str] = None, 650 tags: Optional[Union[str, Sequence[str]]] = None, 651 ) -> FetchTracesResponse: 652 """Fetch a list of traces in the current project matching the given parameters. 653 654 Args: 655 page (Optional[int]): Page number, starts at 1. Defaults to None. 656 limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None. 657 name (Optional[str]): Filter by name of traces. Defaults to None. 658 user_id (Optional[str]): Filter by user_id. Defaults to None. 659 session_id (Optional[str]): Filter by session_id. Defaults to None. 660 from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None. 661 to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None. 662 order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None. 663 tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None. 664 665 Returns: 666 FetchTracesResponse, list of traces on `data` and metadata on `meta`. 667 668 Raises: 669 Exception: If an error occurred during the request. 670 """ 671 try: 672 self.log.debug( 673 f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}" 674 ) 675 res = self.client.trace.list( 676 page=page, 677 limit=limit, 678 name=name, 679 user_id=user_id, 680 session_id=session_id, 681 from_timestamp=from_timestamp, 682 to_timestamp=to_timestamp, 683 order_by=order_by, 684 tags=tags, 685 ) 686 return FetchTracesResponse(data=res.data, meta=res.meta) 687 except Exception as e: 688 handle_fern_exception(e) 689 raise e 690 691 def get_traces( 692 self, 693 *, 694 page: Optional[int] = None, 695 limit: Optional[int] = None, 696 user_id: Optional[str] = None, 697 name: Optional[str] = None, 698 session_id: Optional[str] = None, 699 from_timestamp: Optional[dt.datetime] = None, 700 to_timestamp: Optional[dt.datetime] = None, 701 order_by: Optional[str] = None, 702 tags: Optional[Union[str, Sequence[str]]] = None, 703 ) -> Traces: 704 """Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead. 705 706 Args: 707 page (Optional[int]): Page number, starts at 1. Defaults to None. 708 limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None. 709 name (Optional[str]): Filter by name of traces. Defaults to None. 710 user_id (Optional[str]): Filter by user_id. Defaults to None. 711 session_id (Optional[str]): Filter by session_id. Defaults to None. 712 from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None. 713 to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None. 714 order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None. 715 tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None. 716 717 Returns: 718 List of Traces 719 720 Raises: 721 Exception: If an error occurred during the request. 722 """ 723 warnings.warn( 724 "get_traces is deprecated, use fetch_traces instead.", 725 DeprecationWarning, 726 ) 727 try: 728 self.log.debug( 729 f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}" 730 ) 731 return self.client.trace.list( 732 page=page, 733 limit=limit, 734 name=name, 735 user_id=user_id, 736 session_id=session_id, 737 from_timestamp=from_timestamp, 738 to_timestamp=to_timestamp, 739 order_by=order_by, 740 tags=tags, 741 ) 742 except Exception as e: 743 handle_fern_exception(e) 744 raise e 745 746 def fetch_observations( 747 self, 748 *, 749 page: typing.Optional[int] = None, 750 limit: typing.Optional[int] = None, 751 name: typing.Optional[str] = None, 752 user_id: typing.Optional[str] = None, 753 trace_id: typing.Optional[str] = None, 754 parent_observation_id: typing.Optional[str] = None, 755 from_start_time: typing.Optional[dt.datetime] = None, 756 to_start_time: typing.Optional[dt.datetime] = None, 757 type: typing.Optional[str] = None, 758 ) -> FetchObservationsResponse: 759 """Get a list of observations in the current project matching the given parameters. 760 761 Args: 762 page (Optional[int]): Page number of the observations to return. Defaults to None. 763 limit (Optional[int]): Maximum number of observations to return. Defaults to None. 764 name (Optional[str]): Name of the observations to return. Defaults to None. 765 user_id (Optional[str]): User identifier. Defaults to None. 766 trace_id (Optional[str]): Trace identifier. Defaults to None. 767 parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None. 768 from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None. 769 to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None. 770 type (Optional[str]): Type of the observation. Defaults to None. 771 772 Returns: 773 FetchObservationsResponse, list of observations on `data` and metadata on `meta`. 774 775 Raises: 776 Exception: If an error occurred during the request. 777 """ 778 try: 779 self.log.debug( 780 f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}" 781 ) 782 res = self.client.observations.get_many( 783 page=page, 784 limit=limit, 785 name=name, 786 user_id=user_id, 787 trace_id=trace_id, 788 parent_observation_id=parent_observation_id, 789 from_start_time=from_start_time, 790 to_start_time=to_start_time, 791 type=type, 792 ) 793 return FetchObservationsResponse(data=res.data, meta=res.meta) 794 except Exception as e: 795 self.log.exception(e) 796 raise e 797 798 def get_observations( 799 self, 800 *, 801 page: typing.Optional[int] = None, 802 limit: typing.Optional[int] = None, 803 name: typing.Optional[str] = None, 804 user_id: typing.Optional[str] = None, 805 trace_id: typing.Optional[str] = None, 806 parent_observation_id: typing.Optional[str] = None, 807 from_start_time: typing.Optional[dt.datetime] = None, 808 to_start_time: typing.Optional[dt.datetime] = None, 809 type: typing.Optional[str] = None, 810 ) -> ObservationsViews: 811 """Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead. 812 813 Args: 814 page (Optional[int]): Page number of the observations to return. Defaults to None. 815 limit (Optional[int]): Maximum number of observations to return. Defaults to None. 816 name (Optional[str]): Name of the observations to return. Defaults to None. 817 user_id (Optional[str]): User identifier. Defaults to None. 818 trace_id (Optional[str]): Trace identifier. Defaults to None. 819 parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None. 820 from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None. 821 to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None. 822 type (Optional[str]): Type of the observation. Defaults to None. 823 824 Returns: 825 List of ObservationsViews: List of observations in the project matching the given parameters. 826 827 Raises: 828 Exception: If an error occurred during the request. 829 """ 830 warnings.warn( 831 "get_observations is deprecated, use fetch_observations instead.", 832 DeprecationWarning, 833 ) 834 try: 835 self.log.debug( 836 f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}" 837 ) 838 return self.client.observations.get_many( 839 page=page, 840 limit=limit, 841 name=name, 842 user_id=user_id, 843 trace_id=trace_id, 844 parent_observation_id=parent_observation_id, 845 from_start_time=from_start_time, 846 to_start_time=to_start_time, 847 type=type, 848 ) 849 except Exception as e: 850 handle_fern_exception(e) 851 raise e 852 853 def get_generations( 854 self, 855 *, 856 page: typing.Optional[int] = None, 857 limit: typing.Optional[int] = None, 858 name: typing.Optional[str] = None, 859 user_id: typing.Optional[str] = None, 860 trace_id: typing.Optional[str] = None, 861 from_start_time: typing.Optional[dt.datetime] = None, 862 to_start_time: typing.Optional[dt.datetime] = None, 863 parent_observation_id: typing.Optional[str] = None, 864 ) -> ObservationsViews: 865 """Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead. 866 867 Args: 868 page (Optional[int]): Page number of the generations to return. Defaults to None. 869 limit (Optional[int]): Maximum number of generations to return. Defaults to None. 870 name (Optional[str]): Name of the generations to return. Defaults to None. 871 user_id (Optional[str]): User identifier of the generations to return. Defaults to None. 872 trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None. 873 from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None. 874 to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None. 875 parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None. 876 877 Returns: 878 List of ObservationsViews: List of generations in the project matching the given parameters. 879 880 Raises: 881 Exception: If an error occurred during the request. 882 """ 883 warnings.warn( 884 "get_generations is deprecated, use `fetch_observations(type='GENERATION')` instead.", 885 DeprecationWarning, 886 ) 887 return self.get_observations( 888 page=page, 889 limit=limit, 890 name=name, 891 user_id=user_id, 892 trace_id=trace_id, 893 parent_observation_id=parent_observation_id, 894 from_start_time=from_start_time, 895 to_start_time=to_start_time, 896 type="GENERATION", 897 ) 898 899 def fetch_observation( 900 self, 901 id: str, 902 ) -> FetchObservationResponse: 903 """Get an observation in the current project with the given identifier. 904 905 Args: 906 id: The identifier of the observation to fetch. 907 908 Returns: 909 FetchObservationResponse: The observation with the given id on `data`. 910 911 Raises: 912 Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request. 913 """ 914 try: 915 self.log.debug(f"Getting observation {id}") 916 observation = self.client.observations.get(id) 917 return FetchObservationResponse(data=observation) 918 except Exception as e: 919 handle_fern_exception(e) 920 raise e 921 922 def fetch_media(self, id: str) -> FetchMediaResponse: 923 """Get media content by ID. 924 925 Args: 926 id: The identifier of the media content to fetch. 927 928 Returns: 929 FetchMediaResponse: The media data of the given id on `data`. 930 931 Raises: 932 Exception: If the media content with the given id could not be found within the authenticated project or if an error occurred during the request. 933 """ 934 try: 935 return FetchMediaResponse(data=self.client.media.get(id)) 936 except Exception as e: 937 handle_fern_exception(e) 938 raise e 939 940 def resolve_media_references( 941 self, 942 *, 943 obj: Any, 944 resolve_with: Literal["base64_data_uri"], 945 max_depth: int = 10, 946 content_fetch_timeout_seconds: int = 10, 947 ): 948 """Replace media reference strings in an object with base64 data URIs. 949 950 This method recursively traverses an object (up to max_depth) looking for media reference strings 951 in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using 952 the provided Langfuse client and replaces the reference string with a base64 data URI. 953 954 If fetching media content fails for a reference string, a warning is logged and the reference 955 string is left unchanged. 956 957 Args: 958 obj: The object to process. Can be a primitive value, array, or nested object. 959 If the object has a __dict__ attribute, a dict will be returned instead of the original object type. 960 resolve_with: The representation of the media content to replace the media reference string with. 961 Currently only "base64_data_uri" is supported. 962 max_depth: int: The maximum depth to traverse the object. Default is 10. 963 content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 10. 964 965 Returns: 966 A deep copy of the input object with all media references replaced with base64 data URIs where possible. 967 If the input object has a __dict__ attribute, a dict will be returned instead of the original object type. 968 969 Example: 970 obj = { 971 "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@", 972 "nested": { 973 "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@" 974 } 975 } 976 977 result = await LangfuseMedia.resolve_media_references(obj, langfuse_client) 978 979 # Result: 980 # { 981 # "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", 982 # "nested": { 983 # "pdf": "data:application/pdf;base64,JVBERi0xLjcK..." 984 # } 985 # } 986 """ 987 return LangfuseMedia.resolve_media_references( 988 langfuse_client=self, 989 obj=obj, 990 resolve_with=resolve_with, 991 max_depth=max_depth, 992 content_fetch_timeout_seconds=content_fetch_timeout_seconds, 993 ) 994 995 def get_observation( 996 self, 997 id: str, 998 ) -> Observation: 999 """Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead. 1000 1001 Args: 1002 id: The identifier of the observation to fetch. 1003 1004 Raises: 1005 Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request. 1006 """ 1007 warnings.warn( 1008 "get_observation is deprecated, use fetch_observation instead.", 1009 DeprecationWarning, 1010 ) 1011 try: 1012 self.log.debug(f"Getting observation {id}") 1013 return self.client.observations.get(id) 1014 except Exception as e: 1015 handle_fern_exception(e) 1016 raise e 1017 1018 def fetch_sessions( 1019 self, 1020 *, 1021 page: typing.Optional[int] = None, 1022 limit: typing.Optional[int] = None, 1023 from_timestamp: typing.Optional[dt.datetime] = None, 1024 to_timestamp: typing.Optional[dt.datetime] = None, 1025 ) -> FetchSessionsResponse: 1026 """Get a list of sessions in the current project. 1027 1028 Args: 1029 page (Optional[int]): Page number of the sessions to return. Defaults to None. 1030 limit (Optional[int]): Maximum number of sessions to return. Defaults to None. 1031 from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None. 1032 to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None. 1033 1034 Returns: 1035 FetchSessionsResponse, list of sessions on `data` and metadata on `meta`. 1036 1037 Raises: 1038 Exception: If an error occurred during the request. 1039 """ 1040 try: 1041 self.log.debug( 1042 f"Getting sessions... {page}, {limit}, {from_timestamp}, {to_timestamp}" 1043 ) 1044 res = self.client.sessions.list( 1045 page=page, 1046 limit=limit, 1047 from_timestamp=from_timestamp, 1048 to_timestamp=to_timestamp, 1049 ) 1050 return FetchSessionsResponse(data=res.data, meta=res.meta) 1051 except Exception as e: 1052 handle_fern_exception(e) 1053 raise e 1054 1055 @overload 1056 def get_prompt( 1057 self, 1058 name: str, 1059 version: Optional[int] = None, 1060 *, 1061 label: Optional[str] = None, 1062 type: Literal["chat"], 1063 cache_ttl_seconds: Optional[int] = None, 1064 fallback: Optional[List[ChatMessageDict]] = None, 1065 max_retries: Optional[int] = None, 1066 fetch_timeout_seconds: Optional[int] = None, 1067 ) -> ChatPromptClient: ... 1068 1069 @overload 1070 def get_prompt( 1071 self, 1072 name: str, 1073 version: Optional[int] = None, 1074 *, 1075 label: Optional[str] = None, 1076 type: Literal["text"] = "text", 1077 cache_ttl_seconds: Optional[int] = None, 1078 fallback: Optional[str] = None, 1079 max_retries: Optional[int] = None, 1080 fetch_timeout_seconds: Optional[int] = None, 1081 ) -> TextPromptClient: ... 1082 1083 def get_prompt( 1084 self, 1085 name: str, 1086 version: Optional[int] = None, 1087 *, 1088 label: Optional[str] = None, 1089 type: Literal["chat", "text"] = "text", 1090 cache_ttl_seconds: Optional[int] = None, 1091 fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None, 1092 max_retries: Optional[int] = None, 1093 fetch_timeout_seconds: Optional[int] = None, 1094 ) -> PromptClient: 1095 """Get a prompt. 1096 1097 This method attempts to fetch the requested prompt from the local cache. If the prompt is not found 1098 in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again 1099 and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will 1100 return the expired prompt as a fallback. 1101 1102 Args: 1103 name (str): The name of the prompt to retrieve. 1104 1105 Keyword Args: 1106 version (Optional[int]): The version of the prompt to retrieve. If no label and version is specified, the `production` label is returned. Specify either version or label, not both. 1107 label: Optional[str]: The label of the prompt to retrieve. If no label and version is specified, the `production` label is returned. Specify either version or label, not both. 1108 cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a 1109 keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0. 1110 type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text". 1111 fallback: Union[Optional[List[ChatMessageDict]], Optional[str]]: The prompt string to return if fetching the prompt fails. Important on the first call where no cached prompt is available. Follows Langfuse prompt formatting with double curly braces for variables. Defaults to None. 1112 max_retries: Optional[int]: The maximum number of retries in case of API/network errors. Defaults to 2. The maximum value is 4. Retries have an exponential backoff with a maximum delay of 10 seconds. 1113 fetch_timeout_seconds: Optional[int]: The timeout in milliseconds for fetching the prompt. Defaults to the default timeout set on the SDK, which is 10 seconds per default. 1114 1115 Returns: 1116 The prompt object retrieved from the cache or directly fetched if not cached or expired of type 1117 - TextPromptClient, if type argument is 'text'. 1118 - ChatPromptClient, if type argument is 'chat'. 1119 1120 Raises: 1121 Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an 1122 expired prompt in the cache, in which case it logs a warning and returns the expired prompt. 1123 """ 1124 if version is not None and label is not None: 1125 raise ValueError("Cannot specify both version and label at the same time.") 1126 1127 if not name: 1128 raise ValueError("Prompt name cannot be empty.") 1129 1130 cache_key = PromptCache.generate_cache_key(name, version=version, label=label) 1131 bounded_max_retries = self._get_bounded_max_retries( 1132 max_retries, default_max_retries=2, max_retries_upper_bound=4 1133 ) 1134 1135 self.log.debug(f"Getting prompt '{cache_key}'") 1136 cached_prompt = self.prompt_cache.get(cache_key) 1137 1138 if cached_prompt is None or cache_ttl_seconds == 0: 1139 self.log.debug( 1140 f"Prompt '{cache_key}' not found in cache or caching disabled." 1141 ) 1142 try: 1143 return self._fetch_prompt_and_update_cache( 1144 name, 1145 version=version, 1146 label=label, 1147 ttl_seconds=cache_ttl_seconds, 1148 max_retries=bounded_max_retries, 1149 fetch_timeout_seconds=fetch_timeout_seconds, 1150 ) 1151 except Exception as e: 1152 if fallback: 1153 self.log.warning( 1154 f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}" 1155 ) 1156 1157 fallback_client_args = { 1158 "name": name, 1159 "prompt": fallback, 1160 "type": type, 1161 "version": version or 0, 1162 "config": {}, 1163 "labels": [label] if label else [], 1164 "tags": [], 1165 } 1166 1167 if type == "text": 1168 return TextPromptClient( 1169 prompt=Prompt_Text(**fallback_client_args), 1170 is_fallback=True, 1171 ) 1172 1173 if type == "chat": 1174 return ChatPromptClient( 1175 prompt=Prompt_Chat(**fallback_client_args), 1176 is_fallback=True, 1177 ) 1178 1179 raise e 1180 1181 if cached_prompt.is_expired(): 1182 self.log.debug(f"Stale prompt '{cache_key}' found in cache.") 1183 try: 1184 # refresh prompt in background thread, refresh_prompt deduplicates tasks 1185 self.log.debug(f"Refreshing prompt '{cache_key}' in background.") 1186 self.prompt_cache.add_refresh_prompt_task( 1187 cache_key, 1188 lambda: self._fetch_prompt_and_update_cache( 1189 name, 1190 version=version, 1191 label=label, 1192 ttl_seconds=cache_ttl_seconds, 1193 max_retries=bounded_max_retries, 1194 fetch_timeout_seconds=fetch_timeout_seconds, 1195 ), 1196 ) 1197 self.log.debug(f"Returning stale prompt '{cache_key}' from cache.") 1198 # return stale prompt 1199 return cached_prompt.value 1200 1201 except Exception as e: 1202 self.log.warning( 1203 f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}" 1204 ) 1205 # creation of refresh prompt task failed, return stale prompt 1206 return cached_prompt.value 1207 1208 return cached_prompt.value 1209 1210 def _fetch_prompt_and_update_cache( 1211 self, 1212 name: str, 1213 *, 1214 version: Optional[int] = None, 1215 label: Optional[str] = None, 1216 ttl_seconds: Optional[int] = None, 1217 max_retries: int, 1218 fetch_timeout_seconds, 1219 ) -> PromptClient: 1220 try: 1221 cache_key = PromptCache.generate_cache_key( 1222 name, version=version, label=label 1223 ) 1224 1225 self.log.debug(f"Fetching prompt '{cache_key}' from server...") 1226 1227 @backoff.on_exception( 1228 backoff.constant, Exception, max_tries=max_retries, logger=None 1229 ) 1230 def fetch_prompts(): 1231 return self.client.prompts.get( 1232 self._url_encode(name), 1233 version=version, 1234 label=label, 1235 request_options={ 1236 "timeout_in_seconds": fetch_timeout_seconds, 1237 } 1238 if fetch_timeout_seconds is not None 1239 else None, 1240 ) 1241 1242 prompt_response = fetch_prompts() 1243 1244 if prompt_response.type == "chat": 1245 prompt = ChatPromptClient(prompt_response) 1246 else: 1247 prompt = TextPromptClient(prompt_response) 1248 1249 self.prompt_cache.set(cache_key, prompt, ttl_seconds) 1250 1251 return prompt 1252 1253 except Exception as e: 1254 self.log.error(f"Error while fetching prompt '{cache_key}': {str(e)}") 1255 raise e 1256 1257 def _get_bounded_max_retries( 1258 self, 1259 max_retries: Optional[int], 1260 *, 1261 default_max_retries: int = 2, 1262 max_retries_upper_bound: int = 4, 1263 ) -> int: 1264 if max_retries is None: 1265 return default_max_retries 1266 1267 bounded_max_retries = min( 1268 max(max_retries, 0), 1269 max_retries_upper_bound, 1270 ) 1271 1272 return bounded_max_retries 1273 1274 @overload 1275 def create_prompt( 1276 self, 1277 *, 1278 name: str, 1279 prompt: List[ChatMessageDict], 1280 is_active: Optional[bool] = None, # deprecated 1281 labels: List[str] = [], 1282 tags: Optional[List[str]] = None, 1283 type: Optional[Literal["chat"]], 1284 config: Optional[Any] = None, 1285 commit_message: Optional[str] = None, 1286 ) -> ChatPromptClient: ... 1287 1288 @overload 1289 def create_prompt( 1290 self, 1291 *, 1292 name: str, 1293 prompt: str, 1294 is_active: Optional[bool] = None, # deprecated 1295 labels: List[str] = [], 1296 tags: Optional[List[str]] = None, 1297 type: Optional[Literal["text"]] = "text", 1298 config: Optional[Any] = None, 1299 commit_message: Optional[str] = None, 1300 ) -> TextPromptClient: ... 1301 1302 def create_prompt( 1303 self, 1304 *, 1305 name: str, 1306 prompt: Union[str, List[ChatMessageDict]], 1307 is_active: Optional[bool] = None, # deprecated 1308 labels: List[str] = [], 1309 tags: Optional[List[str]] = None, 1310 type: Optional[Literal["chat", "text"]] = "text", 1311 config: Optional[Any] = None, 1312 commit_message: Optional[str] = None, 1313 ) -> PromptClient: 1314 """Create a new prompt in Langfuse. 1315 1316 Keyword Args: 1317 name : The name of the prompt to be created. 1318 prompt : The content of the prompt to be created. 1319 is_active [DEPRECATED] : A flag indicating whether the prompt is active or not. This is deprecated and will be removed in a future release. Please use the 'production' label instead. 1320 labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label. 1321 tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt. 1322 config: Additional structured data to be saved with the prompt. Defaults to None. 1323 type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text". 1324 commit_message: Optional string describing the change. 1325 1326 Returns: 1327 TextPromptClient: The prompt if type argument is 'text'. 1328 ChatPromptClient: The prompt if type argument is 'chat'. 1329 """ 1330 try: 1331 self.log.debug(f"Creating prompt {name=}, {version=}, {labels=}") 1332 1333 # Handle deprecated is_active flag 1334 if is_active: 1335 self.log.warning( 1336 "The 'is_active' flag is deprecated and will be removed in a future release. Please use the 'production' label instead." 1337 ) 1338 1339 labels = labels if "production" in labels else labels + ["production"] 1340 1341 if type == "chat": 1342 if not isinstance(prompt, list): 1343 raise ValueError( 1344 "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes." 1345 ) 1346 request = CreatePromptRequest_Chat( 1347 name=name, 1348 prompt=prompt, 1349 labels=labels, 1350 tags=tags, 1351 config=config or {}, 1352 commitMessage=commit_message, 1353 type="chat", 1354 ) 1355 server_prompt = self.client.prompts.create(request=request) 1356 1357 return ChatPromptClient(prompt=server_prompt) 1358 1359 if not isinstance(prompt, str): 1360 raise ValueError("For 'text' type, 'prompt' must be a string.") 1361 1362 request = CreatePromptRequest_Text( 1363 name=name, 1364 prompt=prompt, 1365 labels=labels, 1366 tags=tags, 1367 config=config or {}, 1368 commitMessage=commit_message, 1369 type="text", 1370 ) 1371 1372 server_prompt = self.client.prompts.create(request=request) 1373 return TextPromptClient(prompt=server_prompt) 1374 1375 except Exception as e: 1376 handle_fern_exception(e) 1377 raise e 1378 1379 def update_prompt( 1380 self, 1381 *, 1382 name: str, 1383 version: int, 1384 new_labels: List[str] = [], 1385 ): 1386 """Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name. 1387 1388 Args: 1389 name (str): The name of the prompt to update. 1390 version (int): The version number of the prompt to update. 1391 new_labels (List[str], optional): New labels to assign to the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. Defaults to []. 1392 1393 Returns: 1394 Prompt: The updated prompt from the Langfuse API. 1395 1396 """ 1397 updated_prompt = self.client.prompt_version.update( 1398 name=name, 1399 version=version, 1400 new_labels=new_labels, 1401 ) 1402 self.prompt_cache.invalidate(name) 1403 return updated_prompt 1404 1405 def _url_encode(self, url: str) -> str: 1406 return urllib.parse.quote(url) 1407 1408 def trace( 1409 self, 1410 *, 1411 id: typing.Optional[str] = None, 1412 name: typing.Optional[str] = None, 1413 user_id: typing.Optional[str] = None, 1414 session_id: typing.Optional[str] = None, 1415 version: typing.Optional[str] = None, 1416 input: typing.Optional[typing.Any] = None, 1417 output: typing.Optional[typing.Any] = None, 1418 metadata: typing.Optional[typing.Any] = None, 1419 tags: typing.Optional[typing.List[str]] = None, 1420 timestamp: typing.Optional[dt.datetime] = None, 1421 public: typing.Optional[bool] = None, 1422 **kwargs, 1423 ) -> "StatefulTraceClient": 1424 """Create a trace. 1425 1426 Args: 1427 id: The id of the trace can be set, defaults to a random id. Set it to link traces to external systems or when creating a distributed trace. Traces are upserted on id. 1428 name: Identifier of the trace. Useful for sorting/filtering in the UI. 1429 input: The input of the trace. Can be any JSON object. 1430 output: The output of the trace. Can be any JSON object. 1431 metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API. 1432 user_id: The id of the user that triggered the execution. Used to provide user-level analytics. 1433 session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier. 1434 version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging. 1435 release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging. 1436 tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API. 1437 timestamp: The timestamp of the trace. Defaults to the current time if not provided. 1438 public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project. 1439 **kwargs: Additional keyword arguments that can be included in the trace. 1440 1441 Returns: 1442 StatefulTraceClient: The created trace. 1443 1444 Example: 1445 ```python 1446 from langfuse import Langfuse 1447 1448 langfuse = Langfuse() 1449 1450 trace = langfuse.trace( 1451 name="example-application", 1452 user_id="user-1234") 1453 ) 1454 ``` 1455 """ 1456 new_id = id or str(uuid.uuid4()) 1457 self.trace_id = new_id 1458 try: 1459 new_dict = { 1460 "id": new_id, 1461 "name": name, 1462 "userId": user_id, 1463 "sessionId": session_id 1464 or kwargs.get("sessionId", None), # backward compatibility 1465 "release": self.release, 1466 "version": version, 1467 "metadata": metadata, 1468 "input": input, 1469 "output": output, 1470 "tags": tags, 1471 "timestamp": timestamp or _get_timestamp(), 1472 "public": public, 1473 } 1474 if kwargs is not None: 1475 new_dict.update(kwargs) 1476 1477 new_body = TraceBody(**new_dict) 1478 1479 self.log.debug(f"Creating trace {_filter_io_from_event_body(new_dict)}") 1480 event = { 1481 "id": str(uuid.uuid4()), 1482 "type": "trace-create", 1483 "body": new_body, 1484 } 1485 1486 self.task_manager.add_task( 1487 event, 1488 ) 1489 1490 except Exception as e: 1491 self.log.exception(e) 1492 finally: 1493 self._log_memory_usage() 1494 1495 return StatefulTraceClient( 1496 self.client, new_id, StateType.TRACE, new_id, self.task_manager 1497 ) 1498 1499 def _log_memory_usage(self): 1500 try: 1501 is_malloc_tracing_enabled = bool(int(os.getenv("PYTHONTRACEMALLOC", 0))) 1502 report_interval = int(os.getenv("LANGFUSE_DEBUG_MEMORY_REPORT_INTERVAL", 0)) 1503 top_k_items = int(os.getenv("LANGFUSE_DEBUG_MEMORY_TOP_K", 10)) 1504 1505 if ( 1506 not is_malloc_tracing_enabled 1507 or report_interval <= 0 1508 or round(time.monotonic()) % report_interval != 0 1509 ): 1510 return 1511 1512 snapshot = tracemalloc.take_snapshot().statistics("lineno") 1513 1514 total_memory_usage = sum([stat.size for stat in snapshot]) / 1024 / 1024 1515 memory_usage_total_items = [f"{stat}" for stat in snapshot] 1516 memory_usage_langfuse_items = [ 1517 stat for stat in memory_usage_total_items if "/langfuse/" in stat 1518 ] 1519 1520 logged_memory_usage = { 1521 "all_files": [f"{stat}" for stat in memory_usage_total_items][ 1522 :top_k_items 1523 ], 1524 "langfuse_files": [f"{stat}" for stat in memory_usage_langfuse_items][ 1525 :top_k_items 1526 ], 1527 "total_usage": f"{total_memory_usage:.2f} MB", 1528 "langfuse_queue_length": self.task_manager._ingestion_queue.qsize(), 1529 } 1530 1531 self.log.debug("Memory usage: ", logged_memory_usage) 1532 1533 event = SdkLogBody(log=logged_memory_usage) 1534 self.task_manager.add_task( 1535 { 1536 "id": str(uuid.uuid4()), 1537 "type": "sdk-log", 1538 "timestamp": _get_timestamp(), 1539 "body": event.dict(), 1540 } 1541 ) 1542 1543 except Exception as e: 1544 self.log.exception(e) 1545 1546 @overload 1547 def score( 1548 self, 1549 *, 1550 name: str, 1551 value: float, 1552 data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None, 1553 trace_id: typing.Optional[str] = None, 1554 id: typing.Optional[str] = None, 1555 comment: typing.Optional[str] = None, 1556 observation_id: typing.Optional[str] = None, 1557 config_id: typing.Optional[str] = None, 1558 **kwargs, 1559 ) -> "StatefulClient": ... 1560 1561 @overload 1562 def score( 1563 self, 1564 *, 1565 name: str, 1566 value: str, 1567 data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL", 1568 trace_id: typing.Optional[str] = None, 1569 id: typing.Optional[str] = None, 1570 comment: typing.Optional[str] = None, 1571 observation_id: typing.Optional[str] = None, 1572 config_id: typing.Optional[str] = None, 1573 **kwargs, 1574 ) -> "StatefulClient": ... 1575 1576 def score( 1577 self, 1578 *, 1579 name: str, 1580 value: typing.Union[float, str], 1581 data_type: typing.Optional[ScoreDataType] = None, 1582 trace_id: typing.Optional[str] = None, 1583 id: typing.Optional[str] = None, 1584 comment: typing.Optional[str] = None, 1585 observation_id: typing.Optional[str] = None, 1586 config_id: typing.Optional[str] = None, 1587 **kwargs, 1588 ) -> "StatefulClient": 1589 """Create a score attached to a trace (and optionally an observation). 1590 1591 Args: 1592 name (str): Identifier of the score. 1593 value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores. 1594 data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. 1595 When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores. 1596 trace_id (str): The id of the trace to which the score should be attached. 1597 id (Optional[str]): The id of the score. If not provided, a new UUID is generated. 1598 comment (Optional[str]): Additional context/explanation of the score. 1599 observation_id (Optional[str]): The id of the observation to which the score should be attached. 1600 config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None. 1601 **kwargs: Additional keyword arguments to include in the score. 1602 1603 Returns: 1604 StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided). 1605 1606 Example: 1607 ```python 1608 from langfuse import Langfuse 1609 1610 langfuse = Langfuse() 1611 1612 # Create a trace 1613 trace = langfuse.trace(name="example-application") 1614 1615 # Get id of created trace 1616 trace_id = trace.id 1617 1618 # Add score to the trace 1619 trace = langfuse.score( 1620 trace_id=trace_id, 1621 name="user-explicit-feedback", 1622 value=0.9, 1623 comment="I like how personalized the response is" 1624 ) 1625 ``` 1626 """ 1627 trace_id = trace_id or self.trace_id or str(uuid.uuid4()) 1628 new_id = id or str(uuid.uuid4()) 1629 try: 1630 new_dict = { 1631 "id": new_id, 1632 "trace_id": trace_id, 1633 "observation_id": observation_id, 1634 "name": name, 1635 "value": value, 1636 "data_type": data_type, 1637 "comment": comment, 1638 "config_id": config_id, 1639 **kwargs, 1640 } 1641 1642 self.log.debug(f"Creating score {new_dict}...") 1643 new_body = ScoreBody(**new_dict) 1644 1645 event = { 1646 "id": str(uuid.uuid4()), 1647 "type": "score-create", 1648 "body": new_body, 1649 } 1650 self.task_manager.add_task(event) 1651 1652 except Exception as e: 1653 self.log.exception(e) 1654 finally: 1655 if observation_id is not None: 1656 return StatefulClient( 1657 self.client, 1658 observation_id, 1659 StateType.OBSERVATION, 1660 trace_id, 1661 self.task_manager, 1662 ) 1663 else: 1664 return StatefulClient( 1665 self.client, new_id, StateType.TRACE, new_id, self.task_manager 1666 ) 1667 1668 def span( 1669 self, 1670 *, 1671 id: typing.Optional[str] = None, 1672 trace_id: typing.Optional[str] = None, 1673 parent_observation_id: typing.Optional[str] = None, 1674 name: typing.Optional[str] = None, 1675 start_time: typing.Optional[dt.datetime] = None, 1676 end_time: typing.Optional[dt.datetime] = None, 1677 metadata: typing.Optional[typing.Any] = None, 1678 level: typing.Optional[SpanLevel] = None, 1679 status_message: typing.Optional[str] = None, 1680 input: typing.Optional[typing.Any] = None, 1681 output: typing.Optional[typing.Any] = None, 1682 version: typing.Optional[str] = None, 1683 **kwargs, 1684 ) -> "StatefulSpanClient": 1685 """Create a span. 1686 1687 A span represents durations of units of work in a trace. 1688 Usually, you want to add a span nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id. 1689 1690 If no trace_id is provided, a new trace is created just for this span. 1691 1692 Args: 1693 id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id. 1694 trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated. 1695 parent_observation_id (Optional[str]): The ID of the parent observation, if applicable. 1696 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 1697 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 1698 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 1699 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 1700 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 1701 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 1702 input (Optional[dict]): The input to the span. Can be any JSON object. 1703 output (Optional[dict]): The output to the span. Can be any JSON object. 1704 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 1705 **kwargs: Additional keyword arguments to include in the span. 1706 1707 Returns: 1708 StatefulSpanClient: The created span. 1709 1710 Example: 1711 ```python 1712 from langfuse import Langfuse 1713 1714 langfuse = Langfuse() 1715 1716 trace = langfuse.trace(name = "llm-feature") 1717 1718 # Create a span 1719 retrieval = langfuse.span(name = "retrieval", trace_id = trace.id) 1720 1721 # Create a nested span 1722 nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id) 1723 ``` 1724 """ 1725 new_span_id = id or str(uuid.uuid4()) 1726 new_trace_id = trace_id or str(uuid.uuid4()) 1727 self.trace_id = new_trace_id 1728 try: 1729 span_body = { 1730 "id": new_span_id, 1731 "trace_id": new_trace_id, 1732 "name": name, 1733 "start_time": start_time or _get_timestamp(), 1734 "metadata": metadata, 1735 "input": input, 1736 "output": output, 1737 "level": level, 1738 "status_message": status_message, 1739 "parent_observation_id": parent_observation_id, 1740 "version": version, 1741 "end_time": end_time, 1742 "trace": {"release": self.release}, 1743 **kwargs, 1744 } 1745 1746 if trace_id is None: 1747 self._generate_trace(new_trace_id, name or new_trace_id) 1748 1749 self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...") 1750 1751 span_body = CreateSpanBody(**span_body) 1752 1753 event = { 1754 "id": str(uuid.uuid4()), 1755 "type": "span-create", 1756 "body": span_body, 1757 } 1758 1759 self.task_manager.add_task(event) 1760 1761 except Exception as e: 1762 self.log.exception(e) 1763 finally: 1764 self._log_memory_usage() 1765 1766 return StatefulSpanClient( 1767 self.client, 1768 new_span_id, 1769 StateType.OBSERVATION, 1770 new_trace_id, 1771 self.task_manager, 1772 ) 1773 1774 def event( 1775 self, 1776 *, 1777 id: typing.Optional[str] = None, 1778 trace_id: typing.Optional[str] = None, 1779 parent_observation_id: typing.Optional[str] = None, 1780 name: typing.Optional[str] = None, 1781 start_time: typing.Optional[dt.datetime] = None, 1782 metadata: typing.Optional[typing.Any] = None, 1783 input: typing.Optional[typing.Any] = None, 1784 output: typing.Optional[typing.Any] = None, 1785 level: typing.Optional[SpanLevel] = None, 1786 status_message: typing.Optional[str] = None, 1787 version: typing.Optional[str] = None, 1788 **kwargs, 1789 ) -> "StatefulSpanClient": 1790 """Create an event. 1791 1792 An event represents a discrete event in a trace. 1793 Usually, you want to add a event nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id. 1794 1795 If no trace_id is provided, a new trace is created just for this event. 1796 1797 Args: 1798 id (Optional[str]): The id of the event can be set, otherwise a random id is generated. 1799 trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event. 1800 parent_observation_id (Optional[str]): The ID of the parent observation, if applicable. 1801 name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI. 1802 start_time (Optional[datetime]): The time at which the event started, defaults to the current time. 1803 metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API. 1804 input (Optional[Any]): The input to the event. Can be any JSON object. 1805 output (Optional[Any]): The output to the event. Can be any JSON object. 1806 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 1807 status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event. 1808 version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging. 1809 **kwargs: Additional keyword arguments to include in the event. 1810 1811 Returns: 1812 StatefulSpanClient: The created event. 1813 1814 Example: 1815 ```python 1816 from langfuse import Langfuse 1817 1818 langfuse = Langfuse() 1819 1820 trace = langfuse.trace(name = "llm-feature") 1821 1822 # Create an event 1823 retrieval = langfuse.event(name = "retrieval", trace_id = trace.id) 1824 ``` 1825 """ 1826 event_id = id or str(uuid.uuid4()) 1827 new_trace_id = trace_id or str(uuid.uuid4()) 1828 self.trace_id = new_trace_id 1829 try: 1830 event_body = { 1831 "id": event_id, 1832 "trace_id": new_trace_id, 1833 "name": name, 1834 "start_time": start_time or _get_timestamp(), 1835 "metadata": metadata, 1836 "input": input, 1837 "output": output, 1838 "level": level, 1839 "status_message": status_message, 1840 "parent_observation_id": parent_observation_id, 1841 "version": version, 1842 "trace": {"release": self.release}, 1843 **kwargs, 1844 } 1845 1846 if trace_id is None: 1847 self._generate_trace(new_trace_id, name or new_trace_id) 1848 1849 request = CreateEventBody(**event_body) 1850 1851 event = { 1852 "id": str(uuid.uuid4()), 1853 "type": "event-create", 1854 "body": request, 1855 } 1856 1857 self.log.debug( 1858 f"Creating event {_filter_io_from_event_body(event_body)} ..." 1859 ) 1860 self.task_manager.add_task(event) 1861 1862 except Exception as e: 1863 self.log.exception(e) 1864 finally: 1865 return StatefulSpanClient( 1866 self.client, 1867 event_id, 1868 StateType.OBSERVATION, 1869 new_trace_id, 1870 self.task_manager, 1871 ) 1872 1873 def generation( 1874 self, 1875 *, 1876 id: typing.Optional[str] = None, 1877 trace_id: typing.Optional[str] = None, 1878 parent_observation_id: typing.Optional[str] = None, 1879 name: typing.Optional[str] = None, 1880 start_time: typing.Optional[dt.datetime] = None, 1881 end_time: typing.Optional[dt.datetime] = None, 1882 completion_start_time: typing.Optional[dt.datetime] = None, 1883 metadata: typing.Optional[typing.Any] = None, 1884 level: typing.Optional[SpanLevel] = None, 1885 status_message: typing.Optional[str] = None, 1886 version: typing.Optional[str] = None, 1887 model: typing.Optional[str] = None, 1888 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 1889 input: typing.Optional[typing.Any] = None, 1890 output: typing.Optional[typing.Any] = None, 1891 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 1892 usage_details: typing.Optional[typing.Dict[str, int]] = None, 1893 cost_details: typing.Optional[typing.Dict[str, float]] = None, 1894 prompt: typing.Optional[PromptClient] = None, 1895 **kwargs, 1896 ) -> "StatefulGenerationClient": 1897 """Create a generation. 1898 1899 A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI. 1900 1901 Usually, you want to add a generation nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id. 1902 1903 If no trace_id is provided, a new trace is created just for this generation. 1904 1905 Args: 1906 id (Optional[str]): The id of the generation can be set, defaults to random id. 1907 trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created 1908 parent_observation_id (Optional[str]): The ID of the parent observation, if applicable. 1909 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 1910 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 1911 end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`. 1912 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 1913 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 1914 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 1915 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 1916 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 1917 model (Optional[str]): The name of the model used for the generation. 1918 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 1919 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 1920 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 1921 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 1922 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 1923 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 1924 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 1925 **kwargs: Additional keyword arguments to include in the generation. 1926 1927 Returns: 1928 StatefulGenerationClient: The created generation. 1929 1930 Example: 1931 ```python 1932 from langfuse import Langfuse 1933 1934 langfuse = Langfuse() 1935 1936 # Create a generation in Langfuse 1937 generation = langfuse.generation( 1938 name="summary-generation", 1939 model="gpt-3.5-turbo", 1940 model_parameters={"maxTokens": "1000", "temperature": "0.9"}, 1941 input=[{"role": "system", "content": "You are a helpful assistant."}, 1942 {"role": "user", "content": "Please generate a summary of the following documents ..."}], 1943 metadata={"interface": "whatsapp"} 1944 ) 1945 ``` 1946 """ 1947 new_trace_id = trace_id or str(uuid.uuid4()) 1948 new_generation_id = id or str(uuid.uuid4()) 1949 self.trace_id = new_trace_id 1950 try: 1951 generation_body = { 1952 "id": new_generation_id, 1953 "trace_id": new_trace_id, 1954 "release": self.release, 1955 "name": name, 1956 "start_time": start_time or _get_timestamp(), 1957 "metadata": metadata, 1958 "input": input, 1959 "output": output, 1960 "level": level, 1961 "status_message": status_message, 1962 "parent_observation_id": parent_observation_id, 1963 "version": version, 1964 "end_time": end_time, 1965 "completion_start_time": completion_start_time, 1966 "model": model, 1967 "model_parameters": model_parameters, 1968 "usage": _convert_usage_input(usage) if usage is not None else None, 1969 "usage_details": usage_details, 1970 "cost_details": cost_details, 1971 "trace": {"release": self.release}, 1972 **_create_prompt_context(prompt), 1973 **kwargs, 1974 } 1975 1976 if trace_id is None: 1977 trace = { 1978 "id": new_trace_id, 1979 "release": self.release, 1980 "name": name, 1981 } 1982 request = TraceBody(**trace) 1983 1984 event = { 1985 "id": str(uuid.uuid4()), 1986 "type": "trace-create", 1987 "body": request, 1988 } 1989 1990 self.log.debug("Creating trace...") 1991 1992 self.task_manager.add_task(event) 1993 1994 self.log.debug( 1995 f"Creating generation max {_filter_io_from_event_body(generation_body)}..." 1996 ) 1997 request = CreateGenerationBody(**generation_body) 1998 1999 event = { 2000 "id": str(uuid.uuid4()), 2001 "type": "generation-create", 2002 "body": request, 2003 } 2004 2005 self.task_manager.add_task(event) 2006 2007 except Exception as e: 2008 self.log.exception(e) 2009 finally: 2010 return StatefulGenerationClient( 2011 self.client, 2012 new_generation_id, 2013 StateType.OBSERVATION, 2014 new_trace_id, 2015 self.task_manager, 2016 ) 2017 2018 def _generate_trace(self, trace_id: str, name: str): 2019 trace_dict = { 2020 "id": trace_id, 2021 "release": self.release, 2022 "name": name, 2023 } 2024 2025 trace_body = TraceBody(**trace_dict) 2026 2027 event = { 2028 "id": str(uuid.uuid4()), 2029 "type": "trace-create", 2030 "body": trace_body, 2031 } 2032 2033 self.log.debug(f"Creating trace {_filter_io_from_event_body(trace_dict)}...") 2034 self.task_manager.add_task(event) 2035 2036 def join(self): 2037 """Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter. 2038 2039 If called before flushing, consumers might terminate before sending all events to Langfuse API. This method is called at exit of the SKD, right before the Python interpreter closes. 2040 To guarantee all messages have been delivered, you still need to call flush(). 2041 """ 2042 try: 2043 return self.task_manager.join() 2044 except Exception as e: 2045 self.log.exception(e) 2046 2047 def flush(self): 2048 """Flush the internal event queue to the Langfuse API. It blocks until the queue is empty. It should be called when the application shuts down. 2049 2050 Example: 2051 ```python 2052 from langfuse import Langfuse 2053 2054 langfuse = Langfuse() 2055 2056 # Some operations with Langfuse 2057 2058 # Flushing all events to end Langfuse cleanly 2059 langfuse.flush() 2060 ``` 2061 """ 2062 try: 2063 return self.task_manager.flush() 2064 except Exception as e: 2065 self.log.exception(e) 2066 2067 def shutdown(self): 2068 """Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated. 2069 2070 This function calls flush() and join() consecutively resulting in a complete shutdown of the SDK. On success of this function, no more events will be sent to Langfuse API. 2071 As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API. 2072 """ 2073 try: 2074 self.prompt_cache._task_manager.shutdown() 2075 2076 # In logging.py, a handler is attached to the httpx logger. 2077 # To avoid a memory leak on singleton reset, remove all handlers 2078 httpx_logger = logging.getLogger("httpx") 2079 for handler in httpx_logger.handlers: 2080 httpx_logger.removeHandler(handler) 2081 2082 return self.task_manager.shutdown() 2083 except Exception as e: 2084 self.log.exception(e) 2085 2086 2087class StateType(Enum): 2088 """Enum to distinguish observation and trace states. 2089 2090 Attributes: 2091 OBSERVATION (int): Observation state. 2092 TRACE (int): Trace state. 2093 """ 2094 2095 OBSERVATION = 1 2096 TRACE = 0 2097 2098 2099class StatefulClient(object): 2100 """Base class for handling stateful operations in the Langfuse system. 2101 2102 This client is capable of creating different nested Langfuse objects like spans, generations, scores, and events, 2103 associating them with either an observation or a trace based on the specified state type. 2104 2105 Attributes: 2106 client (FernLangfuse): Core interface for Langfuse API interactions. 2107 id (str): Unique identifier of the stateful client (either observation or trace). 2108 state_type (StateType): Enum indicating whether the client is an observation or a trace. 2109 trace_id (str): Id of the trace associated with the stateful client. 2110 task_manager (TaskManager): Manager handling asynchronous tasks for the client. 2111 """ 2112 2113 log = logging.getLogger("langfuse") 2114 2115 def __init__( 2116 self, 2117 client: FernLangfuse, 2118 id: str, 2119 state_type: StateType, 2120 trace_id: str, 2121 task_manager: TaskManager, 2122 ): 2123 """Initialize the StatefulClient. 2124 2125 Args: 2126 client (FernLangfuse): Core interface for Langfuse API interactions. 2127 id (str): Unique identifier of the stateful client (either observation or trace). 2128 state_type (StateType): Enum indicating whether the client is an observation or a trace. 2129 trace_id (str): Id of the trace associated with the stateful client. 2130 task_manager (TaskManager): Manager handling asynchronous tasks for the client. 2131 """ 2132 self.client = client 2133 self.trace_id = trace_id 2134 self.id = id 2135 self.state_type = state_type 2136 self.task_manager = task_manager 2137 2138 def _add_state_to_event(self, body: dict): 2139 if self.state_type == StateType.OBSERVATION: 2140 body["parent_observation_id"] = self.id 2141 body["trace_id"] = self.trace_id 2142 else: 2143 body["trace_id"] = self.id 2144 return body 2145 2146 def _add_default_values(self, body: dict): 2147 if body.get("start_time") is None: 2148 body["start_time"] = _get_timestamp() 2149 return body 2150 2151 def generation( 2152 self, 2153 *, 2154 id: typing.Optional[str] = None, 2155 name: typing.Optional[str] = None, 2156 start_time: typing.Optional[dt.datetime] = None, 2157 end_time: typing.Optional[dt.datetime] = None, 2158 metadata: typing.Optional[typing.Any] = None, 2159 level: typing.Optional[SpanLevel] = None, 2160 status_message: typing.Optional[str] = None, 2161 version: typing.Optional[str] = None, 2162 completion_start_time: typing.Optional[dt.datetime] = None, 2163 model: typing.Optional[str] = None, 2164 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 2165 input: typing.Optional[typing.Any] = None, 2166 output: typing.Optional[typing.Any] = None, 2167 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 2168 usage_details: typing.Optional[typing.Dict[str, int]] = None, 2169 cost_details: typing.Optional[typing.Dict[str, float]] = None, 2170 prompt: typing.Optional[PromptClient] = None, 2171 **kwargs, 2172 ) -> "StatefulGenerationClient": 2173 """Create a generation nested within the current observation or trace. 2174 2175 A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI. 2176 2177 Args: 2178 id (Optional[str]): The id of the generation can be set, defaults to random id. 2179 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 2180 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 2181 end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`. 2182 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 2183 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 2184 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2185 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 2186 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2187 model (Optional[str]): The name of the model used for the generation. 2188 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 2189 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 2190 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 2191 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 2192 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 2193 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 2194 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 2195 **kwargs: Additional keyword arguments to include in the generation. 2196 2197 Returns: 2198 StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations. 2199 2200 Example: 2201 ```python 2202 from langfuse import Langfuse 2203 2204 langfuse = Langfuse() 2205 2206 # Create a trace 2207 trace = langfuse.trace(name = "llm-feature") 2208 2209 # Create a nested generation in Langfuse 2210 generation = trace.generation( 2211 name="summary-generation", 2212 model="gpt-3.5-turbo", 2213 model_parameters={"maxTokens": "1000", "temperature": "0.9"}, 2214 input=[{"role": "system", "content": "You are a helpful assistant."}, 2215 {"role": "user", "content": "Please generate a summary of the following documents ..."}], 2216 metadata={"interface": "whatsapp"} 2217 ) 2218 ``` 2219 """ 2220 generation_id = id or str(uuid.uuid4()) 2221 try: 2222 generation_body = { 2223 "id": generation_id, 2224 "name": name, 2225 "start_time": start_time or _get_timestamp(), 2226 "metadata": metadata, 2227 "level": level, 2228 "status_message": status_message, 2229 "version": version, 2230 "end_time": end_time, 2231 "completion_start_time": completion_start_time, 2232 "model": model, 2233 "model_parameters": model_parameters, 2234 "input": input, 2235 "output": output, 2236 "usage": _convert_usage_input(usage) if usage is not None else None, 2237 "usage_details": usage_details, 2238 "cost_details": cost_details, 2239 **_create_prompt_context(prompt), 2240 **kwargs, 2241 } 2242 2243 generation_body = self._add_state_to_event(generation_body) 2244 new_body = self._add_default_values(generation_body) 2245 2246 new_body = CreateGenerationBody(**new_body) 2247 2248 event = { 2249 "id": str(uuid.uuid4()), 2250 "type": "generation-create", 2251 "body": new_body.dict(exclude_none=True, exclude_unset=False), 2252 } 2253 2254 self.log.debug( 2255 f"Creating generation {_filter_io_from_event_body(generation_body)}..." 2256 ) 2257 self.task_manager.add_task(event) 2258 2259 except Exception as e: 2260 self.log.exception(e) 2261 finally: 2262 return StatefulGenerationClient( 2263 self.client, 2264 generation_id, 2265 StateType.OBSERVATION, 2266 self.trace_id, 2267 task_manager=self.task_manager, 2268 ) 2269 2270 def span( 2271 self, 2272 *, 2273 id: typing.Optional[str] = None, 2274 name: typing.Optional[str] = None, 2275 start_time: typing.Optional[dt.datetime] = None, 2276 end_time: typing.Optional[dt.datetime] = None, 2277 metadata: typing.Optional[typing.Any] = None, 2278 input: typing.Optional[typing.Any] = None, 2279 output: typing.Optional[typing.Any] = None, 2280 level: typing.Optional[SpanLevel] = None, 2281 status_message: typing.Optional[str] = None, 2282 version: typing.Optional[str] = None, 2283 **kwargs, 2284 ) -> "StatefulSpanClient": 2285 """Create a span nested within the current observation or trace. 2286 2287 A span represents durations of units of work in a trace. 2288 2289 Args: 2290 id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id. 2291 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 2292 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 2293 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 2294 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 2295 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2296 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 2297 input (Optional[dict]): The input to the span. Can be any JSON object. 2298 output (Optional[dict]): The output to the span. Can be any JSON object. 2299 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2300 **kwargs: Additional keyword arguments to include in the span. 2301 2302 Returns: 2303 StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations. 2304 2305 Example: 2306 ```python 2307 from langfuse import Langfuse 2308 2309 langfuse = Langfuse() 2310 2311 # Create a trace 2312 trace = langfuse.trace(name = "llm-feature") 2313 2314 # Create a span 2315 retrieval = langfuse.span(name = "retrieval") 2316 ``` 2317 """ 2318 span_id = id or str(uuid.uuid4()) 2319 try: 2320 span_body = { 2321 "id": span_id, 2322 "name": name, 2323 "start_time": start_time or _get_timestamp(), 2324 "metadata": metadata, 2325 "input": input, 2326 "output": output, 2327 "level": level, 2328 "status_message": status_message, 2329 "version": version, 2330 "end_time": end_time, 2331 **kwargs, 2332 } 2333 2334 self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...") 2335 2336 new_dict = self._add_state_to_event(span_body) 2337 new_body = self._add_default_values(new_dict) 2338 2339 event = CreateSpanBody(**new_body) 2340 2341 event = { 2342 "id": str(uuid.uuid4()), 2343 "type": "span-create", 2344 "body": event, 2345 } 2346 2347 self.task_manager.add_task(event) 2348 except Exception as e: 2349 self.log.exception(e) 2350 finally: 2351 return StatefulSpanClient( 2352 self.client, 2353 span_id, 2354 StateType.OBSERVATION, 2355 self.trace_id, 2356 task_manager=self.task_manager, 2357 ) 2358 2359 @overload 2360 def score( 2361 self, 2362 *, 2363 id: typing.Optional[str] = None, 2364 name: str, 2365 value: float, 2366 data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None, 2367 comment: typing.Optional[str] = None, 2368 config_id: typing.Optional[str] = None, 2369 **kwargs, 2370 ) -> "StatefulClient": ... 2371 2372 @overload 2373 def score( 2374 self, 2375 *, 2376 id: typing.Optional[str] = None, 2377 name: str, 2378 value: str, 2379 data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL", 2380 comment: typing.Optional[str] = None, 2381 config_id: typing.Optional[str] = None, 2382 **kwargs, 2383 ) -> "StatefulClient": ... 2384 2385 def score( 2386 self, 2387 *, 2388 id: typing.Optional[str] = None, 2389 name: str, 2390 value: typing.Union[float, str], 2391 data_type: typing.Optional[ScoreDataType] = None, 2392 comment: typing.Optional[str] = None, 2393 config_id: typing.Optional[str] = None, 2394 **kwargs, 2395 ) -> "StatefulClient": 2396 """Create a score attached for the current observation or trace. 2397 2398 Args: 2399 name (str): Identifier of the score. 2400 value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores. 2401 data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. 2402 When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores. 2403 comment (Optional[str]): Additional context/explanation of the score. 2404 id (Optional[str]): The id of the score. If not provided, a new UUID is generated. 2405 config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None. 2406 **kwargs: Additional keyword arguments to include in the score. 2407 2408 Returns: 2409 StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining. 2410 2411 Example: 2412 ```python 2413 from langfuse import Langfuse 2414 2415 langfuse = Langfuse() 2416 2417 # Create a trace 2418 trace = langfuse.trace(name="example-application") 2419 2420 # Add score to the trace 2421 trace = trace.score( 2422 name="user-explicit-feedback", 2423 value=0.8, 2424 comment="I like how personalized the response is" 2425 ) 2426 ``` 2427 """ 2428 score_id = id or str(uuid.uuid4()) 2429 try: 2430 new_score = { 2431 "id": score_id, 2432 "trace_id": self.trace_id, 2433 "name": name, 2434 "value": value, 2435 "data_type": data_type, 2436 "comment": comment, 2437 "config_id": config_id, 2438 **kwargs, 2439 } 2440 2441 self.log.debug(f"Creating score {new_score}...") 2442 2443 new_dict = self._add_state_to_event(new_score) 2444 2445 if self.state_type == StateType.OBSERVATION: 2446 new_dict["observationId"] = self.id 2447 2448 request = ScoreBody(**new_dict) 2449 2450 event = { 2451 "id": str(uuid.uuid4()), 2452 "type": "score-create", 2453 "body": request, 2454 } 2455 2456 self.task_manager.add_task(event) 2457 2458 except Exception as e: 2459 self.log.exception(e) 2460 finally: 2461 return StatefulClient( 2462 self.client, 2463 self.id, 2464 self.state_type, 2465 self.trace_id, 2466 task_manager=self.task_manager, 2467 ) 2468 2469 def event( 2470 self, 2471 *, 2472 id: typing.Optional[str] = None, 2473 name: typing.Optional[str] = None, 2474 start_time: typing.Optional[dt.datetime] = None, 2475 metadata: typing.Optional[typing.Any] = None, 2476 input: typing.Optional[typing.Any] = None, 2477 output: typing.Optional[typing.Any] = None, 2478 level: typing.Optional[SpanLevel] = None, 2479 status_message: typing.Optional[str] = None, 2480 version: typing.Optional[str] = None, 2481 **kwargs, 2482 ) -> "StatefulClient": 2483 """Create an event nested within the current observation or trace. 2484 2485 An event represents a discrete event in a trace. 2486 2487 Args: 2488 id (Optional[str]): The id of the event can be set, otherwise a random id is generated. 2489 name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI. 2490 start_time (Optional[datetime]): The time at which the event started, defaults to the current time. 2491 metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API. 2492 input (Optional[Any]): The input to the event. Can be any JSON object. 2493 output (Optional[Any]): The output to the event. Can be any JSON object. 2494 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2495 status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event. 2496 version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging. 2497 **kwargs: Additional keyword arguments to include in the event. 2498 2499 Returns: 2500 StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations. 2501 2502 Example: 2503 ```python 2504 from langfuse import Langfuse 2505 2506 langfuse = Langfuse() 2507 2508 # Create a trace 2509 trace = langfuse.trace(name = "llm-feature") 2510 2511 # Create an event 2512 retrieval = trace.event(name = "retrieval") 2513 ``` 2514 """ 2515 event_id = id or str(uuid.uuid4()) 2516 try: 2517 event_body = { 2518 "id": event_id, 2519 "name": name, 2520 "start_time": start_time or _get_timestamp(), 2521 "metadata": metadata, 2522 "input": input, 2523 "output": output, 2524 "level": level, 2525 "status_message": status_message, 2526 "version": version, 2527 **kwargs, 2528 } 2529 2530 new_dict = self._add_state_to_event(event_body) 2531 new_body = self._add_default_values(new_dict) 2532 2533 request = CreateEventBody(**new_body) 2534 2535 event = { 2536 "id": str(uuid.uuid4()), 2537 "type": "event-create", 2538 "body": request, 2539 } 2540 2541 self.log.debug( 2542 f"Creating event {_filter_io_from_event_body(event_body)}..." 2543 ) 2544 self.task_manager.add_task(event) 2545 2546 except Exception as e: 2547 self.log.exception(e) 2548 finally: 2549 return StatefulClient( 2550 self.client, 2551 event_id, 2552 StateType.OBSERVATION, 2553 self.trace_id, 2554 self.task_manager, 2555 ) 2556 2557 def get_trace_url(self): 2558 """Get the URL to see the current trace in the Langfuse UI.""" 2559 return f"{self.client._client_wrapper._base_url}/trace/{self.trace_id}" 2560 2561 2562class StatefulGenerationClient(StatefulClient): 2563 """Class for handling stateful operations of generations in the Langfuse system. Inherits from StatefulClient. 2564 2565 This client extends the capabilities of the StatefulClient to specifically handle generation, 2566 allowing for the creation, update, and termination of generation processes in Langfuse. 2567 2568 Attributes: 2569 client (FernLangfuse): Core interface for Langfuse API interaction. 2570 id (str): Unique identifier of the generation. 2571 state_type (StateType): Type of the stateful entity (observation or trace). 2572 trace_id (str): Id of trace associated with the generation. 2573 task_manager (TaskManager): Manager for handling asynchronous tasks. 2574 """ 2575 2576 log = logging.getLogger("langfuse") 2577 2578 def __init__( 2579 self, 2580 client: FernLangfuse, 2581 id: str, 2582 state_type: StateType, 2583 trace_id: str, 2584 task_manager: TaskManager, 2585 ): 2586 """Initialize the StatefulGenerationClient.""" 2587 super().__init__(client, id, state_type, trace_id, task_manager) 2588 2589 # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY 2590 def update( 2591 self, 2592 *, 2593 name: typing.Optional[str] = None, 2594 start_time: typing.Optional[dt.datetime] = None, 2595 end_time: typing.Optional[dt.datetime] = None, 2596 completion_start_time: typing.Optional[dt.datetime] = None, 2597 metadata: typing.Optional[typing.Any] = None, 2598 level: typing.Optional[SpanLevel] = None, 2599 status_message: typing.Optional[str] = None, 2600 version: typing.Optional[str] = None, 2601 model: typing.Optional[str] = None, 2602 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 2603 input: typing.Optional[typing.Any] = None, 2604 output: typing.Optional[typing.Any] = None, 2605 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 2606 usage_details: typing.Optional[typing.Dict[str, int]] = None, 2607 cost_details: typing.Optional[typing.Dict[str, float]] = None, 2608 prompt: typing.Optional[PromptClient] = None, 2609 **kwargs, 2610 ) -> "StatefulGenerationClient": 2611 """Update the generation. 2612 2613 Args: 2614 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 2615 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 2616 end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`. 2617 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 2618 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 2619 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2620 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 2621 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2622 model (Optional[str]): The name of the model used for the generation. 2623 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 2624 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 2625 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 2626 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 2627 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 2628 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 2629 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 2630 **kwargs: Additional keyword arguments to include in the generation. 2631 2632 Returns: 2633 StatefulGenerationClient: The updated generation. Passthrough for chaining. 2634 2635 Example: 2636 ```python 2637 from langfuse import Langfuse 2638 2639 langfuse = Langfuse() 2640 2641 # Create a trace 2642 trace = langfuse.trace(name = "llm-feature") 2643 2644 # Create a nested generation in Langfuse 2645 generation = trace.generation(name="summary-generation") 2646 2647 # Update the generation 2648 generation = generation.update(metadata={"interface": "whatsapp"}) 2649 ``` 2650 """ 2651 try: 2652 generation_body = { 2653 "id": self.id, 2654 "trace_id": self.trace_id, # Included to avoid relying on the order of events sent to the API 2655 "name": name, 2656 "start_time": start_time, 2657 "metadata": metadata, 2658 "level": level, 2659 "status_message": status_message, 2660 "version": version, 2661 "end_time": end_time, 2662 "completion_start_time": completion_start_time, 2663 "model": model, 2664 "model_parameters": model_parameters, 2665 "input": input, 2666 "output": output, 2667 "usage": _convert_usage_input(usage) if usage is not None else None, 2668 "usage_details": usage_details, 2669 "cost_details": cost_details, 2670 **_create_prompt_context(prompt), 2671 **kwargs, 2672 } 2673 2674 self.log.debug( 2675 f"Update generation {_filter_io_from_event_body(generation_body)}..." 2676 ) 2677 2678 request = UpdateGenerationBody(**generation_body) 2679 2680 event = { 2681 "id": str(uuid.uuid4()), 2682 "type": "generation-update", 2683 "body": request.dict(exclude_none=True, exclude_unset=False), 2684 } 2685 2686 self.log.debug( 2687 f"Update generation {_filter_io_from_event_body(generation_body)}..." 2688 ) 2689 self.task_manager.add_task(event) 2690 2691 except Exception as e: 2692 self.log.exception(e) 2693 finally: 2694 return StatefulGenerationClient( 2695 self.client, 2696 self.id, 2697 StateType.OBSERVATION, 2698 self.trace_id, 2699 task_manager=self.task_manager, 2700 ) 2701 2702 def end( 2703 self, 2704 *, 2705 name: typing.Optional[str] = None, 2706 start_time: typing.Optional[dt.datetime] = None, 2707 end_time: typing.Optional[dt.datetime] = None, 2708 completion_start_time: typing.Optional[dt.datetime] = None, 2709 metadata: typing.Optional[typing.Any] = None, 2710 level: typing.Optional[SpanLevel] = None, 2711 status_message: typing.Optional[str] = None, 2712 version: typing.Optional[str] = None, 2713 model: typing.Optional[str] = None, 2714 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 2715 input: typing.Optional[typing.Any] = None, 2716 output: typing.Optional[typing.Any] = None, 2717 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 2718 usage_details: typing.Optional[typing.Dict[str, int]] = None, 2719 cost_details: typing.Optional[typing.Dict[str, float]] = None, 2720 prompt: typing.Optional[PromptClient] = None, 2721 **kwargs, 2722 ) -> "StatefulGenerationClient": 2723 """End the generation, optionally updating its properties. 2724 2725 Args: 2726 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 2727 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 2728 end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time. 2729 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 2730 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 2731 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2732 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 2733 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2734 model (Optional[str]): The name of the model used for the generation. 2735 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 2736 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 2737 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 2738 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 2739 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 2740 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 2741 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 2742 **kwargs: Additional keyword arguments to include in the generation. 2743 2744 Returns: 2745 StatefulGenerationClient: The ended generation. Passthrough for chaining. 2746 2747 Example: 2748 ```python 2749 from langfuse import Langfuse 2750 2751 langfuse = Langfuse() 2752 2753 # Create a trace 2754 trace = langfuse.trace(name = "llm-feature") 2755 2756 # Create a nested generation in Langfuse 2757 generation = trace.generation(name="summary-generation") 2758 2759 # End the generation and update its properties 2760 generation = generation.end(metadata={"interface": "whatsapp"}) 2761 ``` 2762 """ 2763 return self.update( 2764 name=name, 2765 start_time=start_time, 2766 end_time=end_time or _get_timestamp(), 2767 metadata=metadata, 2768 level=level, 2769 status_message=status_message, 2770 version=version, 2771 completion_start_time=completion_start_time, 2772 model=model, 2773 model_parameters=model_parameters, 2774 input=input, 2775 output=output, 2776 usage=usage, 2777 usage_details=usage_details, 2778 cost_details=cost_details, 2779 prompt=prompt, 2780 **kwargs, 2781 ) 2782 2783 2784class StatefulSpanClient(StatefulClient): 2785 """Class for handling stateful operations of spans in the Langfuse system. Inherits from StatefulClient. 2786 2787 Attributes: 2788 client (FernLangfuse): Core interface for Langfuse API interaction. 2789 id (str): Unique identifier of the span. 2790 state_type (StateType): Type of the stateful entity (observation or trace). 2791 trace_id (str): Id of trace associated with the span. 2792 task_manager (TaskManager): Manager for handling asynchronous tasks. 2793 """ 2794 2795 log = logging.getLogger("langfuse") 2796 2797 def __init__( 2798 self, 2799 client: FernLangfuse, 2800 id: str, 2801 state_type: StateType, 2802 trace_id: str, 2803 task_manager: TaskManager, 2804 ): 2805 """Initialize the StatefulSpanClient.""" 2806 super().__init__(client, id, state_type, trace_id, task_manager) 2807 2808 # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY 2809 def update( 2810 self, 2811 *, 2812 name: typing.Optional[str] = None, 2813 start_time: typing.Optional[dt.datetime] = None, 2814 end_time: typing.Optional[dt.datetime] = None, 2815 metadata: typing.Optional[typing.Any] = None, 2816 input: typing.Optional[typing.Any] = None, 2817 output: typing.Optional[typing.Any] = None, 2818 level: typing.Optional[SpanLevel] = None, 2819 status_message: typing.Optional[str] = None, 2820 version: typing.Optional[str] = None, 2821 **kwargs, 2822 ) -> "StatefulSpanClient": 2823 """Update the span. 2824 2825 Args: 2826 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 2827 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 2828 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 2829 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 2830 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2831 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 2832 input (Optional[dict]): The input to the span. Can be any JSON object. 2833 output (Optional[dict]): The output to the span. Can be any JSON object. 2834 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2835 **kwargs: Additional keyword arguments to include in the span. 2836 2837 Returns: 2838 StatefulSpanClient: The updated span. Passthrough for chaining. 2839 2840 Example: 2841 ```python 2842 from langfuse import Langfuse 2843 2844 langfuse = Langfuse() 2845 2846 # Create a trace 2847 trace = langfuse.trace(name = "llm-feature") 2848 2849 # Create a nested span in Langfuse 2850 span = trace.span(name="retrieval") 2851 2852 # Update the span 2853 span = span.update(metadata={"interface": "whatsapp"}) 2854 ``` 2855 """ 2856 try: 2857 span_body = { 2858 "id": self.id, 2859 "trace_id": self.trace_id, # Included to avoid relying on the order of events sent to the API 2860 "name": name, 2861 "start_time": start_time, 2862 "metadata": metadata, 2863 "input": input, 2864 "output": output, 2865 "level": level, 2866 "status_message": status_message, 2867 "version": version, 2868 "end_time": end_time, 2869 **kwargs, 2870 } 2871 self.log.debug(f"Update span {_filter_io_from_event_body(span_body)}...") 2872 2873 request = UpdateSpanBody(**span_body) 2874 2875 event = { 2876 "id": str(uuid.uuid4()), 2877 "type": "span-update", 2878 "body": request, 2879 } 2880 2881 self.task_manager.add_task(event) 2882 except Exception as e: 2883 self.log.exception(e) 2884 finally: 2885 return StatefulSpanClient( 2886 self.client, 2887 self.id, 2888 StateType.OBSERVATION, 2889 self.trace_id, 2890 task_manager=self.task_manager, 2891 ) 2892 2893 def end( 2894 self, 2895 *, 2896 name: typing.Optional[str] = None, 2897 start_time: typing.Optional[dt.datetime] = None, 2898 end_time: typing.Optional[dt.datetime] = None, 2899 metadata: typing.Optional[typing.Any] = None, 2900 input: typing.Optional[typing.Any] = None, 2901 output: typing.Optional[typing.Any] = None, 2902 level: typing.Optional[SpanLevel] = None, 2903 status_message: typing.Optional[str] = None, 2904 version: typing.Optional[str] = None, 2905 **kwargs, 2906 ) -> "StatefulSpanClient": 2907 """End the span, optionally updating its properties. 2908 2909 Args: 2910 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 2911 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 2912 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 2913 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 2914 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2915 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 2916 input (Optional[dict]): The input to the span. Can be any JSON object. 2917 output (Optional[dict]): The output to the span. Can be any JSON object. 2918 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2919 **kwargs: Additional keyword arguments to include in the span. 2920 2921 Returns: 2922 StatefulSpanClient: The updated span. Passthrough for chaining. 2923 2924 Example: 2925 ```python 2926 from langfuse import Langfuse 2927 2928 langfuse = Langfuse() 2929 2930 # Create a trace 2931 trace = langfuse.trace(name = "llm-feature") 2932 2933 # Create a nested span in Langfuse 2934 span = trace.span(name="retrieval") 2935 2936 # End the span and update its properties 2937 span = span.end(metadata={"interface": "whatsapp"}) 2938 ``` 2939 """ 2940 try: 2941 span_body = { 2942 "name": name, 2943 "start_time": start_time, 2944 "metadata": metadata, 2945 "input": input, 2946 "output": output, 2947 "level": level, 2948 "status_message": status_message, 2949 "version": version, 2950 "end_time": end_time or _get_timestamp(), 2951 **kwargs, 2952 } 2953 return self.update(**span_body) 2954 2955 except Exception as e: 2956 self.log.warning(e) 2957 finally: 2958 return StatefulSpanClient( 2959 self.client, 2960 self.id, 2961 StateType.OBSERVATION, 2962 self.trace_id, 2963 task_manager=self.task_manager, 2964 ) 2965 2966 def get_langchain_handler(self, update_parent: bool = False): 2967 """Get langchain callback handler associated with the current span. 2968 2969 Args: 2970 update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run. 2971 2972 Returns: 2973 CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient. 2974 """ 2975 from langfuse.callback import CallbackHandler 2976 2977 return CallbackHandler( 2978 stateful_client=self, update_stateful_client=update_parent 2979 ) 2980 2981 2982class StatefulTraceClient(StatefulClient): 2983 """Class for handling stateful operations of traces in the Langfuse system. Inherits from StatefulClient. 2984 2985 Attributes: 2986 client (FernLangfuse): Core interface for Langfuse API interaction. 2987 id (str): Unique identifier of the trace. 2988 state_type (StateType): Type of the stateful entity (observation or trace). 2989 trace_id (str): The trace ID associated with this client. 2990 task_manager (TaskManager): Manager for handling asynchronous tasks. 2991 """ 2992 2993 log = logging.getLogger("langfuse") 2994 2995 def __init__( 2996 self, 2997 client: FernLangfuse, 2998 id: str, 2999 state_type: StateType, 3000 trace_id: str, 3001 task_manager: TaskManager, 3002 ): 3003 """Initialize the StatefulTraceClient.""" 3004 super().__init__(client, id, state_type, trace_id, task_manager) 3005 self.task_manager = task_manager 3006 3007 def update( 3008 self, 3009 *, 3010 name: typing.Optional[str] = None, 3011 user_id: typing.Optional[str] = None, 3012 session_id: typing.Optional[str] = None, 3013 version: typing.Optional[str] = None, 3014 release: typing.Optional[str] = None, 3015 input: typing.Optional[typing.Any] = None, 3016 output: typing.Optional[typing.Any] = None, 3017 metadata: typing.Optional[typing.Any] = None, 3018 tags: typing.Optional[typing.List[str]] = None, 3019 public: typing.Optional[bool] = None, 3020 **kwargs, 3021 ) -> "StatefulTraceClient": 3022 """Update the trace. 3023 3024 Args: 3025 name: Identifier of the trace. Useful for sorting/filtering in the UI. 3026 input: The input of the trace. Can be any JSON object. 3027 output: The output of the trace. Can be any JSON object. 3028 metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API. 3029 user_id: The id of the user that triggered the execution. Used to provide user-level analytics. 3030 session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier. 3031 version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging. 3032 release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging. 3033 tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API. 3034 public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project. 3035 **kwargs: Additional keyword arguments that can be included in the trace. 3036 3037 Returns: 3038 StatefulTraceClient: The updated trace. Passthrough for chaining. 3039 3040 Example: 3041 ```python 3042 from langfuse import Langfuse 3043 3044 langfuse = Langfuse() 3045 3046 # Create a trace 3047 trace = langfuse.trace( 3048 name="example-application", 3049 user_id="user-1234") 3050 ) 3051 3052 # Update the trace 3053 trace = trace.update( 3054 output={"result": "success"}, 3055 metadata={"interface": "whatsapp"} 3056 ) 3057 ``` 3058 """ 3059 try: 3060 trace_body = { 3061 "id": self.id, 3062 "name": name, 3063 "userId": user_id, 3064 "sessionId": session_id 3065 or kwargs.get("sessionId", None), # backward compatibility 3066 "version": version, 3067 "release": release, 3068 "input": input, 3069 "output": output, 3070 "metadata": metadata, 3071 "public": public, 3072 "tags": tags, 3073 **kwargs, 3074 } 3075 self.log.debug(f"Update trace {_filter_io_from_event_body(trace_body)}...") 3076 3077 request = TraceBody(**trace_body) 3078 3079 event = { 3080 "id": str(uuid.uuid4()), 3081 "type": "trace-create", 3082 "body": request, 3083 } 3084 3085 self.task_manager.add_task(event) 3086 3087 except Exception as e: 3088 self.log.exception(e) 3089 finally: 3090 return StatefulTraceClient( 3091 self.client, 3092 self.id, 3093 StateType.TRACE, 3094 self.trace_id, 3095 task_manager=self.task_manager, 3096 ) 3097 3098 def get_langchain_handler(self, update_parent: bool = False): 3099 """Get langchain callback handler associated with the current trace. 3100 3101 This method creates and returns a CallbackHandler instance, linking it with the current 3102 trace. Use this if you want to group multiple Langchain runs within a single trace. 3103 3104 Args: 3105 update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run. 3106 3107 Raises: 3108 ImportError: If the 'langchain' module is not installed, indicating missing functionality. 3109 3110 Returns: 3111 CallbackHandler: Langchain callback handler linked to the current trace. 3112 3113 Example: 3114 ```python 3115 from langfuse import Langfuse 3116 3117 langfuse = Langfuse() 3118 3119 # Create a trace 3120 trace = langfuse.trace(name = "llm-feature") 3121 3122 # Get a langchain callback handler 3123 handler = trace.get_langchain_handler() 3124 ``` 3125 """ 3126 try: 3127 from langfuse.callback import CallbackHandler 3128 3129 self.log.debug(f"Creating new handler for trace {self.id}") 3130 3131 return CallbackHandler( 3132 stateful_client=self, 3133 debug=self.log.level == logging.DEBUG, 3134 update_stateful_client=update_parent, 3135 ) 3136 except Exception as e: 3137 self.log.exception(e) 3138 3139 def getNewHandler(self): 3140 """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated.""" 3141 return self.get_langchain_handler() 3142 3143 3144class DatasetItemClient: 3145 """Class for managing dataset items in Langfuse. 3146 3147 Args: 3148 id (str): Unique identifier of the dataset item. 3149 status (DatasetStatus): The status of the dataset item. Can be either 'ACTIVE' or 'ARCHIVED'. 3150 input (Any): Input data of the dataset item. 3151 expected_output (Optional[Any]): Expected output of the dataset item. 3152 metadata (Optional[Any]): Additional metadata of the dataset item. 3153 source_trace_id (Optional[str]): Identifier of the source trace. 3154 source_observation_id (Optional[str]): Identifier of the source observation. 3155 dataset_id (str): Identifier of the dataset to which this item belongs. 3156 dataset_name (str): Name of the dataset to which this item belongs. 3157 created_at (datetime): Timestamp of dataset item creation. 3158 updated_at (datetime): Timestamp of the last update to the dataset item. 3159 langfuse (Langfuse): Instance of Langfuse client for API interactions. 3160 3161 Example: 3162 ```python 3163 from langfuse import Langfuse 3164 3165 langfuse = Langfuse() 3166 3167 dataset = langfuse.get_dataset("<dataset_name>") 3168 3169 for item in dataset.items: 3170 # Generate a completion using the input of every item 3171 completion, generation = llm_app.run(item.input) 3172 3173 # Evaluate the completion 3174 generation.score( 3175 name="example-score", 3176 value=1 3177 ) 3178 ``` 3179 """ 3180 3181 log = logging.getLogger("langfuse") 3182 3183 id: str 3184 status: DatasetStatus 3185 input: typing.Any 3186 expected_output: typing.Optional[typing.Any] 3187 metadata: Optional[Any] 3188 source_trace_id: typing.Optional[str] 3189 source_observation_id: typing.Optional[str] 3190 dataset_id: str 3191 dataset_name: str 3192 created_at: dt.datetime 3193 updated_at: dt.datetime 3194 3195 langfuse: Langfuse 3196 3197 def __init__(self, dataset_item: DatasetItem, langfuse: Langfuse): 3198 """Initialize the DatasetItemClient.""" 3199 self.id = dataset_item.id 3200 self.status = dataset_item.status 3201 self.input = dataset_item.input 3202 self.expected_output = dataset_item.expected_output 3203 self.metadata = dataset_item.metadata 3204 self.source_trace_id = dataset_item.source_trace_id 3205 self.source_observation_id = dataset_item.source_observation_id 3206 self.dataset_id = dataset_item.dataset_id 3207 self.dataset_name = dataset_item.dataset_name 3208 self.created_at = dataset_item.created_at 3209 self.updated_at = dataset_item.updated_at 3210 3211 self.langfuse = langfuse 3212 3213 def flush(self, observation: StatefulClient, run_name: str): 3214 """Flushes an observations task manager's queue. 3215 3216 Used before creating a dataset run item to ensure all events are persistent. 3217 3218 Args: 3219 observation (StatefulClient): The observation or trace client associated with the dataset item. 3220 run_name (str): The name of the dataset run. 3221 """ 3222 observation.task_manager.flush() 3223 3224 def link( 3225 self, 3226 trace_or_observation: typing.Union[StatefulClient, str, None], 3227 run_name: str, 3228 run_metadata: Optional[Any] = None, 3229 run_description: Optional[str] = None, 3230 trace_id: Optional[str] = None, 3231 observation_id: Optional[str] = None, 3232 ): 3233 """Link the dataset item to observation within a specific dataset run. Creates a dataset run item. 3234 3235 Args: 3236 trace_or_observation (Union[StatefulClient, str, None]): The trace or observation object to link. Deprecated: can also be an observation ID. 3237 run_name (str): The name of the dataset run. 3238 run_metadata (Optional[Any]): Additional metadata to include in dataset run. 3239 run_description (Optional[str]): Description of the dataset run. 3240 trace_id (Optional[str]): The trace ID to link to the dataset item. Set trace_or_observation to None if trace_id is provided. 3241 observation_id (Optional[str]): The observation ID to link to the dataset item (optional). Set trace_or_observation to None if trace_id is provided. 3242 """ 3243 parsed_trace_id: str = None 3244 parsed_observation_id: str = None 3245 3246 if isinstance(trace_or_observation, StatefulClient): 3247 # flush the queue before creating the dataset run item 3248 # to ensure that all events are persisted. 3249 if trace_or_observation.state_type == StateType.TRACE: 3250 parsed_trace_id = trace_or_observation.trace_id 3251 elif trace_or_observation.state_type == StateType.OBSERVATION: 3252 parsed_observation_id = trace_or_observation.id 3253 parsed_trace_id = trace_or_observation.trace_id 3254 # legacy support for observation_id 3255 elif isinstance(trace_or_observation, str): 3256 parsed_observation_id = trace_or_observation 3257 elif trace_or_observation is None: 3258 if trace_id is not None: 3259 parsed_trace_id = trace_id 3260 if observation_id is not None: 3261 parsed_observation_id = observation_id 3262 else: 3263 raise ValueError( 3264 "trace_id must be provided if trace_or_observation is None" 3265 ) 3266 else: 3267 raise ValueError( 3268 "trace_or_observation (arg) or trace_id (kwarg) must be provided to link the dataset item" 3269 ) 3270 3271 self.log.debug( 3272 f"Creating dataset run item: {run_name} {self.id} {parsed_trace_id} {parsed_observation_id}" 3273 ) 3274 self.langfuse.client.dataset_run_items.create( 3275 request=CreateDatasetRunItemRequest( 3276 runName=run_name, 3277 datasetItemId=self.id, 3278 traceId=parsed_trace_id, 3279 observationId=parsed_observation_id, 3280 metadata=run_metadata, 3281 runDescription=run_description, 3282 ) 3283 ) 3284 3285 def get_langchain_handler( 3286 self, 3287 *, 3288 run_name: str, 3289 run_description: Optional[str] = None, 3290 run_metadata: Optional[Any] = None, 3291 ): 3292 """Create and get a langchain callback handler linked to this dataset item. 3293 3294 Args: 3295 run_name (str): The name of the dataset run to be used in the callback handler. 3296 run_description (Optional[str]): Description of the dataset run. 3297 run_metadata (Optional[Any]): Additional metadata to include in dataset run. 3298 3299 Returns: 3300 CallbackHandler: An instance of CallbackHandler linked to the dataset item. 3301 """ 3302 metadata = { 3303 "dataset_item_id": self.id, 3304 "run_name": run_name, 3305 "dataset_id": self.dataset_id, 3306 } 3307 trace = self.langfuse.trace(name="dataset-run", metadata=metadata) 3308 3309 self.link( 3310 trace, run_name, run_metadata=run_metadata, run_description=run_description 3311 ) 3312 3313 return trace.get_langchain_handler(update_parent=True) 3314 3315 @contextmanager 3316 def observe( 3317 self, 3318 *, 3319 run_name: str, 3320 run_description: Optional[str] = None, 3321 run_metadata: Optional[Any] = None, 3322 trace_id: Optional[str] = None, 3323 ): 3324 """Observes a dataset run within the Langfuse client. 3325 3326 Args: 3327 run_name (str): The name of the dataset run. 3328 root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created. 3329 run_description (Optional[str]): The description of the dataset run. 3330 run_metadata (Optional[Any]): Additional metadata for the dataset run. 3331 3332 Yields: 3333 StatefulTraceClient: The trace associated with the dataset run. 3334 """ 3335 from langfuse.decorators import langfuse_context 3336 3337 root_trace_id = trace_id or str(uuid.uuid4()) 3338 3339 langfuse_context._set_root_trace_id(root_trace_id) 3340 3341 try: 3342 yield root_trace_id 3343 3344 finally: 3345 self.link( 3346 run_name=run_name, 3347 run_metadata=run_metadata, 3348 run_description=run_description, 3349 trace_or_observation=None, 3350 trace_id=root_trace_id, 3351 ) 3352 3353 @contextmanager 3354 def observe_llama_index( 3355 self, 3356 *, 3357 run_name: str, 3358 run_description: Optional[str] = None, 3359 run_metadata: Optional[Any] = None, 3360 llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {}, 3361 ): 3362 """Context manager for observing LlamaIndex operations linked to this dataset item. 3363 3364 This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging 3365 and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all 3366 operations performed within the context are linked to the appropriate dataset item and run in Langfuse. 3367 3368 Args: 3369 run_name (str): The name of the dataset run. 3370 run_description (Optional[str]): Description of the dataset run. Defaults to None. 3371 run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None. 3372 llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass 3373 to the LlamaIndex integration constructor. Defaults to an empty dictionary. 3374 3375 Yields: 3376 LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations. 3377 3378 Example: 3379 ```python 3380 dataset_item = dataset.items[0] 3381 3382 with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler: 3383 # Perform LlamaIndex operations here 3384 some_llama_index_operation() 3385 ``` 3386 3387 Raises: 3388 ImportError: If required modules for LlamaIndex integration are not available. 3389 """ 3390 metadata = { 3391 "dataset_item_id": self.id, 3392 "run_name": run_name, 3393 "dataset_id": self.dataset_id, 3394 } 3395 trace = self.langfuse.trace(name="dataset-run", metadata=metadata) 3396 self.link( 3397 trace, run_name, run_metadata=run_metadata, run_description=run_description 3398 ) 3399 3400 try: 3401 import llama_index.core 3402 from llama_index.core import Settings 3403 from llama_index.core.callbacks import CallbackManager 3404 3405 from langfuse.llama_index import LlamaIndexCallbackHandler 3406 3407 callback_handler = LlamaIndexCallbackHandler( 3408 **llama_index_integration_constructor_kwargs, 3409 ) 3410 callback_handler.set_root(trace, update_root=True) 3411 3412 # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler 3413 # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it 3414 prev_global_handler = llama_index.core.global_handler 3415 prev_langfuse_handler = None 3416 3417 if isinstance(prev_global_handler, LlamaIndexCallbackHandler): 3418 llama_index.core.global_handler = None 3419 3420 if Settings.callback_manager is None: 3421 Settings.callback_manager = CallbackManager([callback_handler]) 3422 else: 3423 for handler in Settings.callback_manager.handlers: 3424 if isinstance(handler, LlamaIndexCallbackHandler): 3425 prev_langfuse_handler = handler 3426 Settings.callback_manager.remove_handler(handler) 3427 3428 Settings.callback_manager.add_handler(callback_handler) 3429 3430 except Exception as e: 3431 self.log.exception(e) 3432 3433 try: 3434 yield callback_handler 3435 finally: 3436 # Reset the handlers 3437 Settings.callback_manager.remove_handler(callback_handler) 3438 if prev_langfuse_handler is not None: 3439 Settings.callback_manager.add_handler(prev_langfuse_handler) 3440 3441 llama_index.core.global_handler = prev_global_handler 3442 3443 def get_llama_index_handler( 3444 self, 3445 *, 3446 run_name: str, 3447 run_description: Optional[str] = None, 3448 run_metadata: Optional[Any] = None, 3449 llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {}, 3450 ): 3451 """Create and get a llama-index callback handler linked to this dataset item. 3452 3453 Args: 3454 run_name (str): The name of the dataset run to be used in the callback handler. 3455 run_description (Optional[str]): Description of the dataset run. 3456 run_metadata (Optional[Any]): Additional metadata to include in dataset run. 3457 llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor. 3458 3459 Returns: 3460 LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item. 3461 """ 3462 metadata = { 3463 "dataset_item_id": self.id, 3464 "run_name": run_name, 3465 "dataset_id": self.dataset_id, 3466 } 3467 trace = self.langfuse.trace(name="dataset-run", metadata=metadata) 3468 3469 self.link( 3470 trace, run_name, run_metadata=run_metadata, run_description=run_description 3471 ) 3472 3473 try: 3474 from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler 3475 3476 callback_handler = LlamaIndexCallbackHandler( 3477 **llama_index_integration_constructor_kwargs, 3478 ) 3479 callback_handler.set_root(trace, update_root=True) 3480 3481 return callback_handler 3482 except Exception as e: 3483 self.log.exception(e) 3484 3485 3486class DatasetClient: 3487 """Class for managing datasets in Langfuse. 3488 3489 Attributes: 3490 id (str): Unique identifier of the dataset. 3491 name (str): Name of the dataset. 3492 description (Optional[str]): Description of the dataset. 3493 metadata (Optional[typing.Any]): Additional metadata of the dataset. 3494 project_id (str): Identifier of the project to which the dataset belongs. 3495 dataset_name (str): Name of the dataset. 3496 created_at (datetime): Timestamp of dataset creation. 3497 updated_at (datetime): Timestamp of the last update to the dataset. 3498 items (List[DatasetItemClient]): List of dataset items associated with the dataset. 3499 runs (List[str]): List of dataset runs associated with the dataset. Deprecated. 3500 3501 Example: 3502 Print the input of each dataset item in a dataset. 3503 ```python 3504 from langfuse import Langfuse 3505 3506 langfuse = Langfuse() 3507 3508 dataset = langfuse.get_dataset("<dataset_name>") 3509 3510 for item in dataset.items: 3511 print(item.input) 3512 ``` 3513 """ 3514 3515 id: str 3516 name: str 3517 description: Optional[str] 3518 project_id: str 3519 dataset_name: str # for backward compatibility, to be deprecated 3520 metadata: Optional[Any] 3521 created_at: dt.datetime 3522 updated_at: dt.datetime 3523 items: typing.List[DatasetItemClient] 3524 runs: typing.List[str] = [] # deprecated 3525 3526 def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]): 3527 """Initialize the DatasetClient.""" 3528 self.id = dataset.id 3529 self.name = dataset.name 3530 self.description = dataset.description 3531 self.project_id = dataset.project_id 3532 self.metadata = dataset.metadata 3533 self.dataset_name = dataset.name # for backward compatibility, to be deprecated 3534 self.created_at = dataset.created_at 3535 self.updated_at = dataset.updated_at 3536 self.items = items 3537 3538 3539def _filter_io_from_event_body(event_body: Dict[str, Any]): 3540 return { 3541 k: v for k, v in event_body.items() if k not in ("input", "output", "metadata") 3542 }
91@dataclass 92class FetchTracesResponse: 93 """Response object for fetch_traces method.""" 94 95 data: typing.List[TraceWithDetails] 96 meta: MetaResponse
Response object for fetch_traces method.
99@dataclass 100class FetchTraceResponse: 101 """Response object for fetch_trace method.""" 102 103 data: TraceWithFullDetails
Response object for fetch_trace method.
106@dataclass 107class FetchObservationsResponse: 108 """Response object for fetch_observations method.""" 109 110 data: typing.List[ObservationsView] 111 meta: MetaResponse
Response object for fetch_observations method.
114@dataclass 115class FetchObservationResponse: 116 """Response object for fetch_observation method.""" 117 118 data: Observation
Response object for fetch_observation method.
121@dataclass 122class FetchMediaResponse: 123 """Response object for fetch_media method.""" 124 125 data: GetMediaResponse
Response object for fetch_media method.
128@dataclass 129class FetchSessionsResponse: 130 """Response object for fetch_sessions method.""" 131 132 data: typing.List[Session] 133 meta: MetaResponse
Response object for fetch_sessions method.
136class Langfuse(object): 137 """Langfuse Python client. 138 139 Attributes: 140 log (logging.Logger): Logger for the Langfuse client. 141 base_url (str): Base URL of the Langfuse API, serving as the root address for API endpoint construction. 142 httpx_client (httpx.Client): HTTPX client utilized for executing requests to the Langfuse API. 143 client (FernLangfuse): Core interface for Langfuse API interaction. 144 task_manager (TaskManager): Task Manager dedicated to handling asynchronous tasks. 145 release (str): Identifies the release number or hash of the application. 146 prompt_cache (PromptCache): A cache for efficiently storing and retrieving PromptClient instances. 147 148 Example: 149 Initiating the Langfuse client should always be first step to use Langfuse. 150 ```python 151 import os 152 from langfuse import Langfuse 153 154 # Set the public and secret keys as environment variables 155 os.environ['LANGFUSE_PUBLIC_KEY'] = public_key 156 os.environ['LANGFUSE_SECRET_KEY'] = secret_key 157 158 # Initialize the Langfuse client using the credentials 159 langfuse = Langfuse() 160 ``` 161 """ 162 163 log = logging.getLogger("langfuse") 164 """Logger for the Langfuse client.""" 165 166 host: str 167 """Host of Langfuse API.""" 168 169 project_id: Optional[str] 170 """Project ID of the Langfuse project associated with the API keys provided.""" 171 172 def __init__( 173 self, 174 public_key: Optional[str] = None, 175 secret_key: Optional[str] = None, 176 host: Optional[str] = None, 177 release: Optional[str] = None, 178 debug: bool = False, 179 threads: Optional[int] = None, 180 flush_at: Optional[int] = None, 181 flush_interval: Optional[float] = None, 182 max_retries: Optional[int] = None, 183 timeout: Optional[int] = None, # seconds 184 sdk_integration: Optional[str] = "default", 185 httpx_client: Optional[httpx.Client] = None, 186 enabled: Optional[bool] = True, 187 sample_rate: Optional[float] = None, 188 mask: Optional[MaskFunction] = None, 189 ): 190 """Initialize the Langfuse client. 191 192 Args: 193 public_key: Public API key of Langfuse project. Can be set via `LANGFUSE_PUBLIC_KEY` environment variable. 194 secret_key: Secret API key of Langfuse project. Can be set via `LANGFUSE_SECRET_KEY` environment variable. 195 host: Host of Langfuse API. Can be set via `LANGFUSE_HOST` environment variable. Defaults to `https://cloud.langfuse.com`. 196 release: Release number/hash of the application to provide analytics grouped by release. Can be set via `LANGFUSE_RELEASE` environment variable. 197 debug: Enables debug mode for more verbose logging. Can be set via `LANGFUSE_DEBUG` environment variable. 198 threads: Number of consumer threads to execute network requests. Helps scaling the SDK for high load. Only increase this if you run into scaling issues. 199 flush_at: Max batch size that's sent to the API. 200 flush_interval: Max delay until a new batch is sent to the API. 201 max_retries: Max number of retries in case of API/network errors. 202 timeout: Timeout of API requests in seconds. Defaults to 20 seconds. 203 httpx_client: Pass your own httpx client for more customizability of requests. 204 sdk_integration: Used by intgerations that wrap the Langfuse SDK to add context for debugging and support. Not to be used directly. 205 enabled: Enables or disables the Langfuse client. If disabled, all observability calls to the backend will be no-ops. 206 sample_rate: Sampling rate for tracing. If set to 0.2, only 20% of the data will be sent to the backend. Can be set via `LANGFUSE_SAMPLE_RATE` environment variable. 207 mask (langfuse.types.MaskFunction): Masking function for 'input' and 'output' fields in events. Function must take a single keyword argument `data` and return a serializable, masked version of the data. 208 209 Raises: 210 ValueError: If public_key or secret_key are not set and not found in environment variables. 211 212 Example: 213 Initiating the Langfuse client should always be first step to use Langfuse. 214 ```python 215 import os 216 from langfuse import Langfuse 217 218 # Set the public and secret keys as environment variables 219 os.environ['LANGFUSE_PUBLIC_KEY'] = public_key 220 os.environ['LANGFUSE_SECRET_KEY'] = secret_key 221 222 # Initialize the Langfuse client using the credentials 223 langfuse = Langfuse() 224 ``` 225 """ 226 self.enabled = enabled 227 public_key = public_key or os.environ.get("LANGFUSE_PUBLIC_KEY") 228 secret_key = secret_key or os.environ.get("LANGFUSE_SECRET_KEY") 229 sample_rate = ( 230 sample_rate 231 if sample_rate 232 is not None # needs explicit None check, as 0 is a valid value 233 else float(os.environ.get("LANGFUSE_SAMPLE_RATE", 1.0)) 234 ) 235 236 if sample_rate is not None and ( 237 sample_rate > 1 or sample_rate < 0 238 ): # default value 1 will be set in the taskmanager 239 self.enabled = False 240 self.log.warning( 241 "Langfuse client is disabled since the sample rate provided is not between 0 and 1." 242 ) 243 244 threads = threads or int(os.environ.get("LANGFUSE_THREADS", 1)) 245 flush_at = flush_at or int(os.environ.get("LANGFUSE_FLUSH_AT", 15)) 246 flush_interval = flush_interval or float( 247 os.environ.get("LANGFUSE_FLUSH_INTERVAL", 0.5) 248 ) 249 250 max_retries = max_retries or int(os.environ.get("LANGFUSE_MAX_RETRIES", 3)) 251 timeout = timeout or int(os.environ.get("LANGFUSE_TIMEOUT", 20)) 252 253 if not self.enabled: 254 self.log.warning( 255 "Langfuse client is disabled. No observability data will be sent." 256 ) 257 258 elif not public_key: 259 self.enabled = False 260 self.log.warning( 261 "Langfuse client is disabled since no public_key was provided as a parameter or environment variable 'LANGFUSE_PUBLIC_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client" 262 ) 263 264 elif not secret_key: 265 self.enabled = False 266 self.log.warning( 267 "Langfuse client is disabled since no secret_key was provided as a parameter or environment variable 'LANGFUSE_SECRET_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client" 268 ) 269 270 set_debug = debug if debug else (os.getenv("LANGFUSE_DEBUG", "False") == "True") 271 272 if set_debug is True: 273 # Ensures that debug level messages are logged when debug mode is on. 274 # Otherwise, defaults to WARNING level. 275 # See https://docs.python.org/3/howto/logging.html#what-happens-if-no-configuration-is-provided 276 logging.basicConfig() 277 # Set level for all loggers under langfuse package 278 logging.getLogger("langfuse").setLevel(logging.DEBUG) 279 280 clean_logger() 281 else: 282 logging.getLogger("langfuse").setLevel(logging.WARNING) 283 clean_logger() 284 285 self.base_url = ( 286 host 287 if host 288 else os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com") 289 ) 290 291 self.httpx_client = httpx_client or httpx.Client(timeout=timeout) 292 293 public_api_client = FernLangfuse( 294 base_url=self.base_url, 295 username=public_key, 296 password=secret_key, 297 x_langfuse_sdk_name="python", 298 x_langfuse_sdk_version=version, 299 x_langfuse_public_key=public_key, 300 httpx_client=self.httpx_client, 301 timeout=timeout, 302 ) 303 async_public_api_client = AsyncFernLangfuse( 304 base_url=self.base_url, 305 username=public_key, 306 password=secret_key, 307 x_langfuse_sdk_name="python", 308 x_langfuse_sdk_version=version, 309 x_langfuse_public_key=public_key, 310 timeout=timeout, 311 ) 312 313 self.api = public_api_client 314 self.client = public_api_client # legacy, to be removed in next major release 315 self.async_api = async_public_api_client 316 317 langfuse_client = LangfuseClient( 318 public_key=public_key, 319 secret_key=secret_key, 320 base_url=self.base_url, 321 version=version, 322 timeout=timeout, 323 session=self.httpx_client, 324 ) 325 326 args = { 327 "threads": threads, 328 "flush_at": flush_at, 329 "flush_interval": flush_interval, 330 "max_retries": max_retries, 331 "client": langfuse_client, 332 "api_client": self.client, 333 "public_key": public_key, 334 "sdk_name": "python", 335 "sdk_version": version, 336 "sdk_integration": sdk_integration, 337 "enabled": self.enabled, 338 "sample_rate": sample_rate, 339 "mask": mask, 340 } 341 342 self.task_manager = TaskManager(**args) 343 344 self.trace_id = None 345 self.project_id = None 346 347 self.release = self._get_release_value(release) 348 349 self.prompt_cache = PromptCache() 350 351 def _get_release_value(self, release: Optional[str] = None) -> Optional[str]: 352 if release: 353 return release 354 elif "LANGFUSE_RELEASE" in os.environ: 355 return os.environ["LANGFUSE_RELEASE"] 356 else: 357 return get_common_release_envs() 358 359 def _get_project_id(self) -> Optional[str]: 360 """Fetch and return the current project id. Persisted across requests. Returns None if no project id is found for api keys.""" 361 if not self.project_id: 362 proj = self.client.projects.get() 363 if not proj.data or not proj.data[0].id: 364 return None 365 366 self.project_id = proj.data[0].id 367 368 return self.project_id 369 370 def get_trace_id(self) -> str: 371 """Get the current trace id.""" 372 return self.trace_id 373 374 def get_trace_url(self) -> str: 375 """Get the URL of the current trace to view it in the Langfuse UI.""" 376 project_id = self._get_project_id() 377 if not project_id: 378 return f"{self.base_url}/trace/{self.trace_id}" 379 380 return f"{self.base_url}/project/{project_id}/traces/{self.trace_id}" 381 382 def get_dataset( 383 self, name: str, *, fetch_items_page_size: Optional[int] = 50 384 ) -> "DatasetClient": 385 """Fetch a dataset by its name. 386 387 Args: 388 name (str): The name of the dataset to fetch. 389 fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50. 390 391 Returns: 392 DatasetClient: The dataset with the given name. 393 """ 394 try: 395 self.log.debug(f"Getting datasets {name}") 396 dataset = self.client.datasets.get(dataset_name=name) 397 398 dataset_items = [] 399 page = 1 400 while True: 401 new_items = self.client.dataset_items.list( 402 dataset_name=self._url_encode(name), 403 page=page, 404 limit=fetch_items_page_size, 405 ) 406 dataset_items.extend(new_items.data) 407 if new_items.meta.total_pages <= page: 408 break 409 page += 1 410 411 items = [DatasetItemClient(i, langfuse=self) for i in dataset_items] 412 413 return DatasetClient(dataset, items=items) 414 except Exception as e: 415 handle_fern_exception(e) 416 raise e 417 418 def get_dataset_item(self, id: str) -> "DatasetItemClient": 419 """Get the dataset item with the given id.""" 420 try: 421 self.log.debug(f"Getting dataset item {id}") 422 dataset_item = self.client.dataset_items.get(id=id) 423 return DatasetItemClient(dataset_item, langfuse=self) 424 except Exception as e: 425 handle_fern_exception(e) 426 raise e 427 428 def auth_check(self) -> bool: 429 """Check if the provided credentials (public and secret key) are valid. 430 431 Raises: 432 Exception: If no projects were found for the provided credentials. 433 434 Note: 435 This method is blocking. It is discouraged to use it in production code. 436 """ 437 try: 438 projects = self.client.projects.get() 439 self.log.debug( 440 f"Auth check successful, found {len(projects.data)} projects" 441 ) 442 if len(projects.data) == 0: 443 raise Exception( 444 "Auth check failed, no project found for the keys provided." 445 ) 446 return True 447 448 except Exception as e: 449 handle_fern_exception(e) 450 raise e 451 452 def get_dataset_runs( 453 self, 454 dataset_name: str, 455 *, 456 page: typing.Optional[int] = None, 457 limit: typing.Optional[int] = None, 458 ) -> PaginatedDatasetRuns: 459 """Get all dataset runs. 460 461 Args: 462 dataset_name (str): Name of the dataset. 463 page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None. 464 limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50. 465 466 Returns: 467 PaginatedDatasetRuns: The dataset runs. 468 """ 469 try: 470 self.log.debug("Getting dataset runs") 471 return self.client.datasets.get_runs( 472 dataset_name=self._url_encode(dataset_name), page=page, limit=limit 473 ) 474 except Exception as e: 475 handle_fern_exception(e) 476 raise e 477 478 def get_dataset_run( 479 self, 480 dataset_name: str, 481 dataset_run_name: str, 482 ) -> DatasetRunWithItems: 483 """Get a dataset run. 484 485 Args: 486 dataset_name: Name of the dataset. 487 dataset_run_name: Name of the dataset run. 488 489 Returns: 490 DatasetRunWithItems: The dataset run. 491 """ 492 try: 493 self.log.debug( 494 f"Getting dataset runs for dataset {dataset_name} and run {dataset_run_name}" 495 ) 496 return self.client.datasets.get_run( 497 dataset_name=self._url_encode(dataset_name), 498 run_name=self._url_encode(dataset_run_name), 499 ) 500 except Exception as e: 501 handle_fern_exception(e) 502 raise e 503 504 def create_dataset( 505 self, 506 name: str, 507 description: Optional[str] = None, 508 metadata: Optional[Any] = None, 509 ) -> Dataset: 510 """Create a dataset with the given name on Langfuse. 511 512 Args: 513 name: Name of the dataset to create. 514 description: Description of the dataset. Defaults to None. 515 metadata: Additional metadata. Defaults to None. 516 517 Returns: 518 Dataset: The created dataset as returned by the Langfuse API. 519 """ 520 try: 521 body = CreateDatasetRequest( 522 name=name, description=description, metadata=metadata 523 ) 524 self.log.debug(f"Creating datasets {body}") 525 return self.client.datasets.create(request=body) 526 except Exception as e: 527 handle_fern_exception(e) 528 raise e 529 530 def create_dataset_item( 531 self, 532 dataset_name: str, 533 input: Optional[Any] = None, 534 expected_output: Optional[Any] = None, 535 metadata: Optional[Any] = None, 536 source_trace_id: Optional[str] = None, 537 source_observation_id: Optional[str] = None, 538 status: Optional[DatasetStatus] = None, 539 id: Optional[str] = None, 540 ) -> DatasetItem: 541 """Create a dataset item. 542 543 Upserts if an item with id already exists. 544 545 Args: 546 dataset_name: Name of the dataset in which the dataset item should be created. 547 input: Input data. Defaults to None. Can contain any dict, list or scalar. 548 expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar. 549 metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar. 550 source_trace_id: Id of the source trace. Defaults to None. 551 source_observation_id: Id of the source observation. Defaults to None. 552 status: Status of the dataset item. Defaults to ACTIVE for newly created items. 553 id: Id of the dataset item. Defaults to None. Provide your own id if you want to dedupe dataset items. Id needs to be globally unique and cannot be reused across datasets. 554 555 Returns: 556 DatasetItem: The created dataset item as returned by the Langfuse API. 557 558 Example: 559 ```python 560 from langfuse import Langfuse 561 562 langfuse = Langfuse() 563 564 # Uploading items to the Langfuse dataset named "capital_cities" 565 langfuse.create_dataset_item( 566 dataset_name="capital_cities", 567 input={"input": {"country": "Italy"}}, 568 expected_output={"expected_output": "Rome"}, 569 metadata={"foo": "bar"} 570 ) 571 ``` 572 """ 573 try: 574 body = CreateDatasetItemRequest( 575 datasetName=dataset_name, 576 input=input, 577 expectedOutput=expected_output, 578 metadata=metadata, 579 sourceTraceId=source_trace_id, 580 sourceObservationId=source_observation_id, 581 status=status, 582 id=id, 583 ) 584 self.log.debug(f"Creating dataset item {body}") 585 return self.client.dataset_items.create(request=body) 586 except Exception as e: 587 handle_fern_exception(e) 588 raise e 589 590 def fetch_trace( 591 self, 592 id: str, 593 ) -> FetchTraceResponse: 594 """Fetch a trace via the Langfuse API by its id. 595 596 Args: 597 id: The id of the trace to fetch. 598 599 Returns: 600 FetchTraceResponse: The trace with full details as returned by the Langfuse API on `data`. 601 602 Raises: 603 Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request. 604 """ 605 try: 606 self.log.debug(f"Getting trace {id}") 607 trace = self.client.trace.get(id) 608 return FetchTraceResponse(data=trace) 609 except Exception as e: 610 handle_fern_exception(e) 611 raise e 612 613 def get_trace( 614 self, 615 id: str, 616 ) -> TraceWithFullDetails: 617 """Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead. 618 619 Args: 620 id: The id of the trace to fetch. 621 622 Returns: 623 TraceWithFullDetails: The trace with full details as returned by the Langfuse API. 624 625 Raises: 626 Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request. 627 """ 628 warnings.warn( 629 "get_trace is deprecated, use fetch_trace instead.", 630 DeprecationWarning, 631 ) 632 633 try: 634 self.log.debug(f"Getting trace {id}") 635 return self.client.trace.get(id) 636 except Exception as e: 637 handle_fern_exception(e) 638 raise e 639 640 def fetch_traces( 641 self, 642 *, 643 page: Optional[int] = None, 644 limit: Optional[int] = None, 645 user_id: Optional[str] = None, 646 name: Optional[str] = None, 647 session_id: Optional[str] = None, 648 from_timestamp: Optional[dt.datetime] = None, 649 to_timestamp: Optional[dt.datetime] = None, 650 order_by: Optional[str] = None, 651 tags: Optional[Union[str, Sequence[str]]] = None, 652 ) -> FetchTracesResponse: 653 """Fetch a list of traces in the current project matching the given parameters. 654 655 Args: 656 page (Optional[int]): Page number, starts at 1. Defaults to None. 657 limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None. 658 name (Optional[str]): Filter by name of traces. Defaults to None. 659 user_id (Optional[str]): Filter by user_id. Defaults to None. 660 session_id (Optional[str]): Filter by session_id. Defaults to None. 661 from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None. 662 to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None. 663 order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None. 664 tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None. 665 666 Returns: 667 FetchTracesResponse, list of traces on `data` and metadata on `meta`. 668 669 Raises: 670 Exception: If an error occurred during the request. 671 """ 672 try: 673 self.log.debug( 674 f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}" 675 ) 676 res = self.client.trace.list( 677 page=page, 678 limit=limit, 679 name=name, 680 user_id=user_id, 681 session_id=session_id, 682 from_timestamp=from_timestamp, 683 to_timestamp=to_timestamp, 684 order_by=order_by, 685 tags=tags, 686 ) 687 return FetchTracesResponse(data=res.data, meta=res.meta) 688 except Exception as e: 689 handle_fern_exception(e) 690 raise e 691 692 def get_traces( 693 self, 694 *, 695 page: Optional[int] = None, 696 limit: Optional[int] = None, 697 user_id: Optional[str] = None, 698 name: Optional[str] = None, 699 session_id: Optional[str] = None, 700 from_timestamp: Optional[dt.datetime] = None, 701 to_timestamp: Optional[dt.datetime] = None, 702 order_by: Optional[str] = None, 703 tags: Optional[Union[str, Sequence[str]]] = None, 704 ) -> Traces: 705 """Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead. 706 707 Args: 708 page (Optional[int]): Page number, starts at 1. Defaults to None. 709 limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None. 710 name (Optional[str]): Filter by name of traces. Defaults to None. 711 user_id (Optional[str]): Filter by user_id. Defaults to None. 712 session_id (Optional[str]): Filter by session_id. Defaults to None. 713 from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None. 714 to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None. 715 order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None. 716 tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None. 717 718 Returns: 719 List of Traces 720 721 Raises: 722 Exception: If an error occurred during the request. 723 """ 724 warnings.warn( 725 "get_traces is deprecated, use fetch_traces instead.", 726 DeprecationWarning, 727 ) 728 try: 729 self.log.debug( 730 f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}" 731 ) 732 return self.client.trace.list( 733 page=page, 734 limit=limit, 735 name=name, 736 user_id=user_id, 737 session_id=session_id, 738 from_timestamp=from_timestamp, 739 to_timestamp=to_timestamp, 740 order_by=order_by, 741 tags=tags, 742 ) 743 except Exception as e: 744 handle_fern_exception(e) 745 raise e 746 747 def fetch_observations( 748 self, 749 *, 750 page: typing.Optional[int] = None, 751 limit: typing.Optional[int] = None, 752 name: typing.Optional[str] = None, 753 user_id: typing.Optional[str] = None, 754 trace_id: typing.Optional[str] = None, 755 parent_observation_id: typing.Optional[str] = None, 756 from_start_time: typing.Optional[dt.datetime] = None, 757 to_start_time: typing.Optional[dt.datetime] = None, 758 type: typing.Optional[str] = None, 759 ) -> FetchObservationsResponse: 760 """Get a list of observations in the current project matching the given parameters. 761 762 Args: 763 page (Optional[int]): Page number of the observations to return. Defaults to None. 764 limit (Optional[int]): Maximum number of observations to return. Defaults to None. 765 name (Optional[str]): Name of the observations to return. Defaults to None. 766 user_id (Optional[str]): User identifier. Defaults to None. 767 trace_id (Optional[str]): Trace identifier. Defaults to None. 768 parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None. 769 from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None. 770 to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None. 771 type (Optional[str]): Type of the observation. Defaults to None. 772 773 Returns: 774 FetchObservationsResponse, list of observations on `data` and metadata on `meta`. 775 776 Raises: 777 Exception: If an error occurred during the request. 778 """ 779 try: 780 self.log.debug( 781 f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}" 782 ) 783 res = self.client.observations.get_many( 784 page=page, 785 limit=limit, 786 name=name, 787 user_id=user_id, 788 trace_id=trace_id, 789 parent_observation_id=parent_observation_id, 790 from_start_time=from_start_time, 791 to_start_time=to_start_time, 792 type=type, 793 ) 794 return FetchObservationsResponse(data=res.data, meta=res.meta) 795 except Exception as e: 796 self.log.exception(e) 797 raise e 798 799 def get_observations( 800 self, 801 *, 802 page: typing.Optional[int] = None, 803 limit: typing.Optional[int] = None, 804 name: typing.Optional[str] = None, 805 user_id: typing.Optional[str] = None, 806 trace_id: typing.Optional[str] = None, 807 parent_observation_id: typing.Optional[str] = None, 808 from_start_time: typing.Optional[dt.datetime] = None, 809 to_start_time: typing.Optional[dt.datetime] = None, 810 type: typing.Optional[str] = None, 811 ) -> ObservationsViews: 812 """Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead. 813 814 Args: 815 page (Optional[int]): Page number of the observations to return. Defaults to None. 816 limit (Optional[int]): Maximum number of observations to return. Defaults to None. 817 name (Optional[str]): Name of the observations to return. Defaults to None. 818 user_id (Optional[str]): User identifier. Defaults to None. 819 trace_id (Optional[str]): Trace identifier. Defaults to None. 820 parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None. 821 from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None. 822 to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None. 823 type (Optional[str]): Type of the observation. Defaults to None. 824 825 Returns: 826 List of ObservationsViews: List of observations in the project matching the given parameters. 827 828 Raises: 829 Exception: If an error occurred during the request. 830 """ 831 warnings.warn( 832 "get_observations is deprecated, use fetch_observations instead.", 833 DeprecationWarning, 834 ) 835 try: 836 self.log.debug( 837 f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}" 838 ) 839 return self.client.observations.get_many( 840 page=page, 841 limit=limit, 842 name=name, 843 user_id=user_id, 844 trace_id=trace_id, 845 parent_observation_id=parent_observation_id, 846 from_start_time=from_start_time, 847 to_start_time=to_start_time, 848 type=type, 849 ) 850 except Exception as e: 851 handle_fern_exception(e) 852 raise e 853 854 def get_generations( 855 self, 856 *, 857 page: typing.Optional[int] = None, 858 limit: typing.Optional[int] = None, 859 name: typing.Optional[str] = None, 860 user_id: typing.Optional[str] = None, 861 trace_id: typing.Optional[str] = None, 862 from_start_time: typing.Optional[dt.datetime] = None, 863 to_start_time: typing.Optional[dt.datetime] = None, 864 parent_observation_id: typing.Optional[str] = None, 865 ) -> ObservationsViews: 866 """Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead. 867 868 Args: 869 page (Optional[int]): Page number of the generations to return. Defaults to None. 870 limit (Optional[int]): Maximum number of generations to return. Defaults to None. 871 name (Optional[str]): Name of the generations to return. Defaults to None. 872 user_id (Optional[str]): User identifier of the generations to return. Defaults to None. 873 trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None. 874 from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None. 875 to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None. 876 parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None. 877 878 Returns: 879 List of ObservationsViews: List of generations in the project matching the given parameters. 880 881 Raises: 882 Exception: If an error occurred during the request. 883 """ 884 warnings.warn( 885 "get_generations is deprecated, use `fetch_observations(type='GENERATION')` instead.", 886 DeprecationWarning, 887 ) 888 return self.get_observations( 889 page=page, 890 limit=limit, 891 name=name, 892 user_id=user_id, 893 trace_id=trace_id, 894 parent_observation_id=parent_observation_id, 895 from_start_time=from_start_time, 896 to_start_time=to_start_time, 897 type="GENERATION", 898 ) 899 900 def fetch_observation( 901 self, 902 id: str, 903 ) -> FetchObservationResponse: 904 """Get an observation in the current project with the given identifier. 905 906 Args: 907 id: The identifier of the observation to fetch. 908 909 Returns: 910 FetchObservationResponse: The observation with the given id on `data`. 911 912 Raises: 913 Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request. 914 """ 915 try: 916 self.log.debug(f"Getting observation {id}") 917 observation = self.client.observations.get(id) 918 return FetchObservationResponse(data=observation) 919 except Exception as e: 920 handle_fern_exception(e) 921 raise e 922 923 def fetch_media(self, id: str) -> FetchMediaResponse: 924 """Get media content by ID. 925 926 Args: 927 id: The identifier of the media content to fetch. 928 929 Returns: 930 FetchMediaResponse: The media data of the given id on `data`. 931 932 Raises: 933 Exception: If the media content with the given id could not be found within the authenticated project or if an error occurred during the request. 934 """ 935 try: 936 return FetchMediaResponse(data=self.client.media.get(id)) 937 except Exception as e: 938 handle_fern_exception(e) 939 raise e 940 941 def resolve_media_references( 942 self, 943 *, 944 obj: Any, 945 resolve_with: Literal["base64_data_uri"], 946 max_depth: int = 10, 947 content_fetch_timeout_seconds: int = 10, 948 ): 949 """Replace media reference strings in an object with base64 data URIs. 950 951 This method recursively traverses an object (up to max_depth) looking for media reference strings 952 in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using 953 the provided Langfuse client and replaces the reference string with a base64 data URI. 954 955 If fetching media content fails for a reference string, a warning is logged and the reference 956 string is left unchanged. 957 958 Args: 959 obj: The object to process. Can be a primitive value, array, or nested object. 960 If the object has a __dict__ attribute, a dict will be returned instead of the original object type. 961 resolve_with: The representation of the media content to replace the media reference string with. 962 Currently only "base64_data_uri" is supported. 963 max_depth: int: The maximum depth to traverse the object. Default is 10. 964 content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 10. 965 966 Returns: 967 A deep copy of the input object with all media references replaced with base64 data URIs where possible. 968 If the input object has a __dict__ attribute, a dict will be returned instead of the original object type. 969 970 Example: 971 obj = { 972 "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@", 973 "nested": { 974 "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@" 975 } 976 } 977 978 result = await LangfuseMedia.resolve_media_references(obj, langfuse_client) 979 980 # Result: 981 # { 982 # "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", 983 # "nested": { 984 # "pdf": "data:application/pdf;base64,JVBERi0xLjcK..." 985 # } 986 # } 987 """ 988 return LangfuseMedia.resolve_media_references( 989 langfuse_client=self, 990 obj=obj, 991 resolve_with=resolve_with, 992 max_depth=max_depth, 993 content_fetch_timeout_seconds=content_fetch_timeout_seconds, 994 ) 995 996 def get_observation( 997 self, 998 id: str, 999 ) -> Observation: 1000 """Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead. 1001 1002 Args: 1003 id: The identifier of the observation to fetch. 1004 1005 Raises: 1006 Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request. 1007 """ 1008 warnings.warn( 1009 "get_observation is deprecated, use fetch_observation instead.", 1010 DeprecationWarning, 1011 ) 1012 try: 1013 self.log.debug(f"Getting observation {id}") 1014 return self.client.observations.get(id) 1015 except Exception as e: 1016 handle_fern_exception(e) 1017 raise e 1018 1019 def fetch_sessions( 1020 self, 1021 *, 1022 page: typing.Optional[int] = None, 1023 limit: typing.Optional[int] = None, 1024 from_timestamp: typing.Optional[dt.datetime] = None, 1025 to_timestamp: typing.Optional[dt.datetime] = None, 1026 ) -> FetchSessionsResponse: 1027 """Get a list of sessions in the current project. 1028 1029 Args: 1030 page (Optional[int]): Page number of the sessions to return. Defaults to None. 1031 limit (Optional[int]): Maximum number of sessions to return. Defaults to None. 1032 from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None. 1033 to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None. 1034 1035 Returns: 1036 FetchSessionsResponse, list of sessions on `data` and metadata on `meta`. 1037 1038 Raises: 1039 Exception: If an error occurred during the request. 1040 """ 1041 try: 1042 self.log.debug( 1043 f"Getting sessions... {page}, {limit}, {from_timestamp}, {to_timestamp}" 1044 ) 1045 res = self.client.sessions.list( 1046 page=page, 1047 limit=limit, 1048 from_timestamp=from_timestamp, 1049 to_timestamp=to_timestamp, 1050 ) 1051 return FetchSessionsResponse(data=res.data, meta=res.meta) 1052 except Exception as e: 1053 handle_fern_exception(e) 1054 raise e 1055 1056 @overload 1057 def get_prompt( 1058 self, 1059 name: str, 1060 version: Optional[int] = None, 1061 *, 1062 label: Optional[str] = None, 1063 type: Literal["chat"], 1064 cache_ttl_seconds: Optional[int] = None, 1065 fallback: Optional[List[ChatMessageDict]] = None, 1066 max_retries: Optional[int] = None, 1067 fetch_timeout_seconds: Optional[int] = None, 1068 ) -> ChatPromptClient: ... 1069 1070 @overload 1071 def get_prompt( 1072 self, 1073 name: str, 1074 version: Optional[int] = None, 1075 *, 1076 label: Optional[str] = None, 1077 type: Literal["text"] = "text", 1078 cache_ttl_seconds: Optional[int] = None, 1079 fallback: Optional[str] = None, 1080 max_retries: Optional[int] = None, 1081 fetch_timeout_seconds: Optional[int] = None, 1082 ) -> TextPromptClient: ... 1083 1084 def get_prompt( 1085 self, 1086 name: str, 1087 version: Optional[int] = None, 1088 *, 1089 label: Optional[str] = None, 1090 type: Literal["chat", "text"] = "text", 1091 cache_ttl_seconds: Optional[int] = None, 1092 fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None, 1093 max_retries: Optional[int] = None, 1094 fetch_timeout_seconds: Optional[int] = None, 1095 ) -> PromptClient: 1096 """Get a prompt. 1097 1098 This method attempts to fetch the requested prompt from the local cache. If the prompt is not found 1099 in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again 1100 and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will 1101 return the expired prompt as a fallback. 1102 1103 Args: 1104 name (str): The name of the prompt to retrieve. 1105 1106 Keyword Args: 1107 version (Optional[int]): The version of the prompt to retrieve. If no label and version is specified, the `production` label is returned. Specify either version or label, not both. 1108 label: Optional[str]: The label of the prompt to retrieve. If no label and version is specified, the `production` label is returned. Specify either version or label, not both. 1109 cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a 1110 keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0. 1111 type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text". 1112 fallback: Union[Optional[List[ChatMessageDict]], Optional[str]]: The prompt string to return if fetching the prompt fails. Important on the first call where no cached prompt is available. Follows Langfuse prompt formatting with double curly braces for variables. Defaults to None. 1113 max_retries: Optional[int]: The maximum number of retries in case of API/network errors. Defaults to 2. The maximum value is 4. Retries have an exponential backoff with a maximum delay of 10 seconds. 1114 fetch_timeout_seconds: Optional[int]: The timeout in milliseconds for fetching the prompt. Defaults to the default timeout set on the SDK, which is 10 seconds per default. 1115 1116 Returns: 1117 The prompt object retrieved from the cache or directly fetched if not cached or expired of type 1118 - TextPromptClient, if type argument is 'text'. 1119 - ChatPromptClient, if type argument is 'chat'. 1120 1121 Raises: 1122 Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an 1123 expired prompt in the cache, in which case it logs a warning and returns the expired prompt. 1124 """ 1125 if version is not None and label is not None: 1126 raise ValueError("Cannot specify both version and label at the same time.") 1127 1128 if not name: 1129 raise ValueError("Prompt name cannot be empty.") 1130 1131 cache_key = PromptCache.generate_cache_key(name, version=version, label=label) 1132 bounded_max_retries = self._get_bounded_max_retries( 1133 max_retries, default_max_retries=2, max_retries_upper_bound=4 1134 ) 1135 1136 self.log.debug(f"Getting prompt '{cache_key}'") 1137 cached_prompt = self.prompt_cache.get(cache_key) 1138 1139 if cached_prompt is None or cache_ttl_seconds == 0: 1140 self.log.debug( 1141 f"Prompt '{cache_key}' not found in cache or caching disabled." 1142 ) 1143 try: 1144 return self._fetch_prompt_and_update_cache( 1145 name, 1146 version=version, 1147 label=label, 1148 ttl_seconds=cache_ttl_seconds, 1149 max_retries=bounded_max_retries, 1150 fetch_timeout_seconds=fetch_timeout_seconds, 1151 ) 1152 except Exception as e: 1153 if fallback: 1154 self.log.warning( 1155 f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}" 1156 ) 1157 1158 fallback_client_args = { 1159 "name": name, 1160 "prompt": fallback, 1161 "type": type, 1162 "version": version or 0, 1163 "config": {}, 1164 "labels": [label] if label else [], 1165 "tags": [], 1166 } 1167 1168 if type == "text": 1169 return TextPromptClient( 1170 prompt=Prompt_Text(**fallback_client_args), 1171 is_fallback=True, 1172 ) 1173 1174 if type == "chat": 1175 return ChatPromptClient( 1176 prompt=Prompt_Chat(**fallback_client_args), 1177 is_fallback=True, 1178 ) 1179 1180 raise e 1181 1182 if cached_prompt.is_expired(): 1183 self.log.debug(f"Stale prompt '{cache_key}' found in cache.") 1184 try: 1185 # refresh prompt in background thread, refresh_prompt deduplicates tasks 1186 self.log.debug(f"Refreshing prompt '{cache_key}' in background.") 1187 self.prompt_cache.add_refresh_prompt_task( 1188 cache_key, 1189 lambda: self._fetch_prompt_and_update_cache( 1190 name, 1191 version=version, 1192 label=label, 1193 ttl_seconds=cache_ttl_seconds, 1194 max_retries=bounded_max_retries, 1195 fetch_timeout_seconds=fetch_timeout_seconds, 1196 ), 1197 ) 1198 self.log.debug(f"Returning stale prompt '{cache_key}' from cache.") 1199 # return stale prompt 1200 return cached_prompt.value 1201 1202 except Exception as e: 1203 self.log.warning( 1204 f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}" 1205 ) 1206 # creation of refresh prompt task failed, return stale prompt 1207 return cached_prompt.value 1208 1209 return cached_prompt.value 1210 1211 def _fetch_prompt_and_update_cache( 1212 self, 1213 name: str, 1214 *, 1215 version: Optional[int] = None, 1216 label: Optional[str] = None, 1217 ttl_seconds: Optional[int] = None, 1218 max_retries: int, 1219 fetch_timeout_seconds, 1220 ) -> PromptClient: 1221 try: 1222 cache_key = PromptCache.generate_cache_key( 1223 name, version=version, label=label 1224 ) 1225 1226 self.log.debug(f"Fetching prompt '{cache_key}' from server...") 1227 1228 @backoff.on_exception( 1229 backoff.constant, Exception, max_tries=max_retries, logger=None 1230 ) 1231 def fetch_prompts(): 1232 return self.client.prompts.get( 1233 self._url_encode(name), 1234 version=version, 1235 label=label, 1236 request_options={ 1237 "timeout_in_seconds": fetch_timeout_seconds, 1238 } 1239 if fetch_timeout_seconds is not None 1240 else None, 1241 ) 1242 1243 prompt_response = fetch_prompts() 1244 1245 if prompt_response.type == "chat": 1246 prompt = ChatPromptClient(prompt_response) 1247 else: 1248 prompt = TextPromptClient(prompt_response) 1249 1250 self.prompt_cache.set(cache_key, prompt, ttl_seconds) 1251 1252 return prompt 1253 1254 except Exception as e: 1255 self.log.error(f"Error while fetching prompt '{cache_key}': {str(e)}") 1256 raise e 1257 1258 def _get_bounded_max_retries( 1259 self, 1260 max_retries: Optional[int], 1261 *, 1262 default_max_retries: int = 2, 1263 max_retries_upper_bound: int = 4, 1264 ) -> int: 1265 if max_retries is None: 1266 return default_max_retries 1267 1268 bounded_max_retries = min( 1269 max(max_retries, 0), 1270 max_retries_upper_bound, 1271 ) 1272 1273 return bounded_max_retries 1274 1275 @overload 1276 def create_prompt( 1277 self, 1278 *, 1279 name: str, 1280 prompt: List[ChatMessageDict], 1281 is_active: Optional[bool] = None, # deprecated 1282 labels: List[str] = [], 1283 tags: Optional[List[str]] = None, 1284 type: Optional[Literal["chat"]], 1285 config: Optional[Any] = None, 1286 commit_message: Optional[str] = None, 1287 ) -> ChatPromptClient: ... 1288 1289 @overload 1290 def create_prompt( 1291 self, 1292 *, 1293 name: str, 1294 prompt: str, 1295 is_active: Optional[bool] = None, # deprecated 1296 labels: List[str] = [], 1297 tags: Optional[List[str]] = None, 1298 type: Optional[Literal["text"]] = "text", 1299 config: Optional[Any] = None, 1300 commit_message: Optional[str] = None, 1301 ) -> TextPromptClient: ... 1302 1303 def create_prompt( 1304 self, 1305 *, 1306 name: str, 1307 prompt: Union[str, List[ChatMessageDict]], 1308 is_active: Optional[bool] = None, # deprecated 1309 labels: List[str] = [], 1310 tags: Optional[List[str]] = None, 1311 type: Optional[Literal["chat", "text"]] = "text", 1312 config: Optional[Any] = None, 1313 commit_message: Optional[str] = None, 1314 ) -> PromptClient: 1315 """Create a new prompt in Langfuse. 1316 1317 Keyword Args: 1318 name : The name of the prompt to be created. 1319 prompt : The content of the prompt to be created. 1320 is_active [DEPRECATED] : A flag indicating whether the prompt is active or not. This is deprecated and will be removed in a future release. Please use the 'production' label instead. 1321 labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label. 1322 tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt. 1323 config: Additional structured data to be saved with the prompt. Defaults to None. 1324 type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text". 1325 commit_message: Optional string describing the change. 1326 1327 Returns: 1328 TextPromptClient: The prompt if type argument is 'text'. 1329 ChatPromptClient: The prompt if type argument is 'chat'. 1330 """ 1331 try: 1332 self.log.debug(f"Creating prompt {name=}, {version=}, {labels=}") 1333 1334 # Handle deprecated is_active flag 1335 if is_active: 1336 self.log.warning( 1337 "The 'is_active' flag is deprecated and will be removed in a future release. Please use the 'production' label instead." 1338 ) 1339 1340 labels = labels if "production" in labels else labels + ["production"] 1341 1342 if type == "chat": 1343 if not isinstance(prompt, list): 1344 raise ValueError( 1345 "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes." 1346 ) 1347 request = CreatePromptRequest_Chat( 1348 name=name, 1349 prompt=prompt, 1350 labels=labels, 1351 tags=tags, 1352 config=config or {}, 1353 commitMessage=commit_message, 1354 type="chat", 1355 ) 1356 server_prompt = self.client.prompts.create(request=request) 1357 1358 return ChatPromptClient(prompt=server_prompt) 1359 1360 if not isinstance(prompt, str): 1361 raise ValueError("For 'text' type, 'prompt' must be a string.") 1362 1363 request = CreatePromptRequest_Text( 1364 name=name, 1365 prompt=prompt, 1366 labels=labels, 1367 tags=tags, 1368 config=config or {}, 1369 commitMessage=commit_message, 1370 type="text", 1371 ) 1372 1373 server_prompt = self.client.prompts.create(request=request) 1374 return TextPromptClient(prompt=server_prompt) 1375 1376 except Exception as e: 1377 handle_fern_exception(e) 1378 raise e 1379 1380 def update_prompt( 1381 self, 1382 *, 1383 name: str, 1384 version: int, 1385 new_labels: List[str] = [], 1386 ): 1387 """Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name. 1388 1389 Args: 1390 name (str): The name of the prompt to update. 1391 version (int): The version number of the prompt to update. 1392 new_labels (List[str], optional): New labels to assign to the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. Defaults to []. 1393 1394 Returns: 1395 Prompt: The updated prompt from the Langfuse API. 1396 1397 """ 1398 updated_prompt = self.client.prompt_version.update( 1399 name=name, 1400 version=version, 1401 new_labels=new_labels, 1402 ) 1403 self.prompt_cache.invalidate(name) 1404 return updated_prompt 1405 1406 def _url_encode(self, url: str) -> str: 1407 return urllib.parse.quote(url) 1408 1409 def trace( 1410 self, 1411 *, 1412 id: typing.Optional[str] = None, 1413 name: typing.Optional[str] = None, 1414 user_id: typing.Optional[str] = None, 1415 session_id: typing.Optional[str] = None, 1416 version: typing.Optional[str] = None, 1417 input: typing.Optional[typing.Any] = None, 1418 output: typing.Optional[typing.Any] = None, 1419 metadata: typing.Optional[typing.Any] = None, 1420 tags: typing.Optional[typing.List[str]] = None, 1421 timestamp: typing.Optional[dt.datetime] = None, 1422 public: typing.Optional[bool] = None, 1423 **kwargs, 1424 ) -> "StatefulTraceClient": 1425 """Create a trace. 1426 1427 Args: 1428 id: The id of the trace can be set, defaults to a random id. Set it to link traces to external systems or when creating a distributed trace. Traces are upserted on id. 1429 name: Identifier of the trace. Useful for sorting/filtering in the UI. 1430 input: The input of the trace. Can be any JSON object. 1431 output: The output of the trace. Can be any JSON object. 1432 metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API. 1433 user_id: The id of the user that triggered the execution. Used to provide user-level analytics. 1434 session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier. 1435 version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging. 1436 release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging. 1437 tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API. 1438 timestamp: The timestamp of the trace. Defaults to the current time if not provided. 1439 public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project. 1440 **kwargs: Additional keyword arguments that can be included in the trace. 1441 1442 Returns: 1443 StatefulTraceClient: The created trace. 1444 1445 Example: 1446 ```python 1447 from langfuse import Langfuse 1448 1449 langfuse = Langfuse() 1450 1451 trace = langfuse.trace( 1452 name="example-application", 1453 user_id="user-1234") 1454 ) 1455 ``` 1456 """ 1457 new_id = id or str(uuid.uuid4()) 1458 self.trace_id = new_id 1459 try: 1460 new_dict = { 1461 "id": new_id, 1462 "name": name, 1463 "userId": user_id, 1464 "sessionId": session_id 1465 or kwargs.get("sessionId", None), # backward compatibility 1466 "release": self.release, 1467 "version": version, 1468 "metadata": metadata, 1469 "input": input, 1470 "output": output, 1471 "tags": tags, 1472 "timestamp": timestamp or _get_timestamp(), 1473 "public": public, 1474 } 1475 if kwargs is not None: 1476 new_dict.update(kwargs) 1477 1478 new_body = TraceBody(**new_dict) 1479 1480 self.log.debug(f"Creating trace {_filter_io_from_event_body(new_dict)}") 1481 event = { 1482 "id": str(uuid.uuid4()), 1483 "type": "trace-create", 1484 "body": new_body, 1485 } 1486 1487 self.task_manager.add_task( 1488 event, 1489 ) 1490 1491 except Exception as e: 1492 self.log.exception(e) 1493 finally: 1494 self._log_memory_usage() 1495 1496 return StatefulTraceClient( 1497 self.client, new_id, StateType.TRACE, new_id, self.task_manager 1498 ) 1499 1500 def _log_memory_usage(self): 1501 try: 1502 is_malloc_tracing_enabled = bool(int(os.getenv("PYTHONTRACEMALLOC", 0))) 1503 report_interval = int(os.getenv("LANGFUSE_DEBUG_MEMORY_REPORT_INTERVAL", 0)) 1504 top_k_items = int(os.getenv("LANGFUSE_DEBUG_MEMORY_TOP_K", 10)) 1505 1506 if ( 1507 not is_malloc_tracing_enabled 1508 or report_interval <= 0 1509 or round(time.monotonic()) % report_interval != 0 1510 ): 1511 return 1512 1513 snapshot = tracemalloc.take_snapshot().statistics("lineno") 1514 1515 total_memory_usage = sum([stat.size for stat in snapshot]) / 1024 / 1024 1516 memory_usage_total_items = [f"{stat}" for stat in snapshot] 1517 memory_usage_langfuse_items = [ 1518 stat for stat in memory_usage_total_items if "/langfuse/" in stat 1519 ] 1520 1521 logged_memory_usage = { 1522 "all_files": [f"{stat}" for stat in memory_usage_total_items][ 1523 :top_k_items 1524 ], 1525 "langfuse_files": [f"{stat}" for stat in memory_usage_langfuse_items][ 1526 :top_k_items 1527 ], 1528 "total_usage": f"{total_memory_usage:.2f} MB", 1529 "langfuse_queue_length": self.task_manager._ingestion_queue.qsize(), 1530 } 1531 1532 self.log.debug("Memory usage: ", logged_memory_usage) 1533 1534 event = SdkLogBody(log=logged_memory_usage) 1535 self.task_manager.add_task( 1536 { 1537 "id": str(uuid.uuid4()), 1538 "type": "sdk-log", 1539 "timestamp": _get_timestamp(), 1540 "body": event.dict(), 1541 } 1542 ) 1543 1544 except Exception as e: 1545 self.log.exception(e) 1546 1547 @overload 1548 def score( 1549 self, 1550 *, 1551 name: str, 1552 value: float, 1553 data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None, 1554 trace_id: typing.Optional[str] = None, 1555 id: typing.Optional[str] = None, 1556 comment: typing.Optional[str] = None, 1557 observation_id: typing.Optional[str] = None, 1558 config_id: typing.Optional[str] = None, 1559 **kwargs, 1560 ) -> "StatefulClient": ... 1561 1562 @overload 1563 def score( 1564 self, 1565 *, 1566 name: str, 1567 value: str, 1568 data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL", 1569 trace_id: typing.Optional[str] = None, 1570 id: typing.Optional[str] = None, 1571 comment: typing.Optional[str] = None, 1572 observation_id: typing.Optional[str] = None, 1573 config_id: typing.Optional[str] = None, 1574 **kwargs, 1575 ) -> "StatefulClient": ... 1576 1577 def score( 1578 self, 1579 *, 1580 name: str, 1581 value: typing.Union[float, str], 1582 data_type: typing.Optional[ScoreDataType] = None, 1583 trace_id: typing.Optional[str] = None, 1584 id: typing.Optional[str] = None, 1585 comment: typing.Optional[str] = None, 1586 observation_id: typing.Optional[str] = None, 1587 config_id: typing.Optional[str] = None, 1588 **kwargs, 1589 ) -> "StatefulClient": 1590 """Create a score attached to a trace (and optionally an observation). 1591 1592 Args: 1593 name (str): Identifier of the score. 1594 value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores. 1595 data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. 1596 When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores. 1597 trace_id (str): The id of the trace to which the score should be attached. 1598 id (Optional[str]): The id of the score. If not provided, a new UUID is generated. 1599 comment (Optional[str]): Additional context/explanation of the score. 1600 observation_id (Optional[str]): The id of the observation to which the score should be attached. 1601 config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None. 1602 **kwargs: Additional keyword arguments to include in the score. 1603 1604 Returns: 1605 StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided). 1606 1607 Example: 1608 ```python 1609 from langfuse import Langfuse 1610 1611 langfuse = Langfuse() 1612 1613 # Create a trace 1614 trace = langfuse.trace(name="example-application") 1615 1616 # Get id of created trace 1617 trace_id = trace.id 1618 1619 # Add score to the trace 1620 trace = langfuse.score( 1621 trace_id=trace_id, 1622 name="user-explicit-feedback", 1623 value=0.9, 1624 comment="I like how personalized the response is" 1625 ) 1626 ``` 1627 """ 1628 trace_id = trace_id or self.trace_id or str(uuid.uuid4()) 1629 new_id = id or str(uuid.uuid4()) 1630 try: 1631 new_dict = { 1632 "id": new_id, 1633 "trace_id": trace_id, 1634 "observation_id": observation_id, 1635 "name": name, 1636 "value": value, 1637 "data_type": data_type, 1638 "comment": comment, 1639 "config_id": config_id, 1640 **kwargs, 1641 } 1642 1643 self.log.debug(f"Creating score {new_dict}...") 1644 new_body = ScoreBody(**new_dict) 1645 1646 event = { 1647 "id": str(uuid.uuid4()), 1648 "type": "score-create", 1649 "body": new_body, 1650 } 1651 self.task_manager.add_task(event) 1652 1653 except Exception as e: 1654 self.log.exception(e) 1655 finally: 1656 if observation_id is not None: 1657 return StatefulClient( 1658 self.client, 1659 observation_id, 1660 StateType.OBSERVATION, 1661 trace_id, 1662 self.task_manager, 1663 ) 1664 else: 1665 return StatefulClient( 1666 self.client, new_id, StateType.TRACE, new_id, self.task_manager 1667 ) 1668 1669 def span( 1670 self, 1671 *, 1672 id: typing.Optional[str] = None, 1673 trace_id: typing.Optional[str] = None, 1674 parent_observation_id: typing.Optional[str] = None, 1675 name: typing.Optional[str] = None, 1676 start_time: typing.Optional[dt.datetime] = None, 1677 end_time: typing.Optional[dt.datetime] = None, 1678 metadata: typing.Optional[typing.Any] = None, 1679 level: typing.Optional[SpanLevel] = None, 1680 status_message: typing.Optional[str] = None, 1681 input: typing.Optional[typing.Any] = None, 1682 output: typing.Optional[typing.Any] = None, 1683 version: typing.Optional[str] = None, 1684 **kwargs, 1685 ) -> "StatefulSpanClient": 1686 """Create a span. 1687 1688 A span represents durations of units of work in a trace. 1689 Usually, you want to add a span nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id. 1690 1691 If no trace_id is provided, a new trace is created just for this span. 1692 1693 Args: 1694 id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id. 1695 trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated. 1696 parent_observation_id (Optional[str]): The ID of the parent observation, if applicable. 1697 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 1698 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 1699 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 1700 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 1701 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 1702 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 1703 input (Optional[dict]): The input to the span. Can be any JSON object. 1704 output (Optional[dict]): The output to the span. Can be any JSON object. 1705 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 1706 **kwargs: Additional keyword arguments to include in the span. 1707 1708 Returns: 1709 StatefulSpanClient: The created span. 1710 1711 Example: 1712 ```python 1713 from langfuse import Langfuse 1714 1715 langfuse = Langfuse() 1716 1717 trace = langfuse.trace(name = "llm-feature") 1718 1719 # Create a span 1720 retrieval = langfuse.span(name = "retrieval", trace_id = trace.id) 1721 1722 # Create a nested span 1723 nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id) 1724 ``` 1725 """ 1726 new_span_id = id or str(uuid.uuid4()) 1727 new_trace_id = trace_id or str(uuid.uuid4()) 1728 self.trace_id = new_trace_id 1729 try: 1730 span_body = { 1731 "id": new_span_id, 1732 "trace_id": new_trace_id, 1733 "name": name, 1734 "start_time": start_time or _get_timestamp(), 1735 "metadata": metadata, 1736 "input": input, 1737 "output": output, 1738 "level": level, 1739 "status_message": status_message, 1740 "parent_observation_id": parent_observation_id, 1741 "version": version, 1742 "end_time": end_time, 1743 "trace": {"release": self.release}, 1744 **kwargs, 1745 } 1746 1747 if trace_id is None: 1748 self._generate_trace(new_trace_id, name or new_trace_id) 1749 1750 self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...") 1751 1752 span_body = CreateSpanBody(**span_body) 1753 1754 event = { 1755 "id": str(uuid.uuid4()), 1756 "type": "span-create", 1757 "body": span_body, 1758 } 1759 1760 self.task_manager.add_task(event) 1761 1762 except Exception as e: 1763 self.log.exception(e) 1764 finally: 1765 self._log_memory_usage() 1766 1767 return StatefulSpanClient( 1768 self.client, 1769 new_span_id, 1770 StateType.OBSERVATION, 1771 new_trace_id, 1772 self.task_manager, 1773 ) 1774 1775 def event( 1776 self, 1777 *, 1778 id: typing.Optional[str] = None, 1779 trace_id: typing.Optional[str] = None, 1780 parent_observation_id: typing.Optional[str] = None, 1781 name: typing.Optional[str] = None, 1782 start_time: typing.Optional[dt.datetime] = None, 1783 metadata: typing.Optional[typing.Any] = None, 1784 input: typing.Optional[typing.Any] = None, 1785 output: typing.Optional[typing.Any] = None, 1786 level: typing.Optional[SpanLevel] = None, 1787 status_message: typing.Optional[str] = None, 1788 version: typing.Optional[str] = None, 1789 **kwargs, 1790 ) -> "StatefulSpanClient": 1791 """Create an event. 1792 1793 An event represents a discrete event in a trace. 1794 Usually, you want to add a event nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id. 1795 1796 If no trace_id is provided, a new trace is created just for this event. 1797 1798 Args: 1799 id (Optional[str]): The id of the event can be set, otherwise a random id is generated. 1800 trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event. 1801 parent_observation_id (Optional[str]): The ID of the parent observation, if applicable. 1802 name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI. 1803 start_time (Optional[datetime]): The time at which the event started, defaults to the current time. 1804 metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API. 1805 input (Optional[Any]): The input to the event. Can be any JSON object. 1806 output (Optional[Any]): The output to the event. Can be any JSON object. 1807 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 1808 status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event. 1809 version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging. 1810 **kwargs: Additional keyword arguments to include in the event. 1811 1812 Returns: 1813 StatefulSpanClient: The created event. 1814 1815 Example: 1816 ```python 1817 from langfuse import Langfuse 1818 1819 langfuse = Langfuse() 1820 1821 trace = langfuse.trace(name = "llm-feature") 1822 1823 # Create an event 1824 retrieval = langfuse.event(name = "retrieval", trace_id = trace.id) 1825 ``` 1826 """ 1827 event_id = id or str(uuid.uuid4()) 1828 new_trace_id = trace_id or str(uuid.uuid4()) 1829 self.trace_id = new_trace_id 1830 try: 1831 event_body = { 1832 "id": event_id, 1833 "trace_id": new_trace_id, 1834 "name": name, 1835 "start_time": start_time or _get_timestamp(), 1836 "metadata": metadata, 1837 "input": input, 1838 "output": output, 1839 "level": level, 1840 "status_message": status_message, 1841 "parent_observation_id": parent_observation_id, 1842 "version": version, 1843 "trace": {"release": self.release}, 1844 **kwargs, 1845 } 1846 1847 if trace_id is None: 1848 self._generate_trace(new_trace_id, name or new_trace_id) 1849 1850 request = CreateEventBody(**event_body) 1851 1852 event = { 1853 "id": str(uuid.uuid4()), 1854 "type": "event-create", 1855 "body": request, 1856 } 1857 1858 self.log.debug( 1859 f"Creating event {_filter_io_from_event_body(event_body)} ..." 1860 ) 1861 self.task_manager.add_task(event) 1862 1863 except Exception as e: 1864 self.log.exception(e) 1865 finally: 1866 return StatefulSpanClient( 1867 self.client, 1868 event_id, 1869 StateType.OBSERVATION, 1870 new_trace_id, 1871 self.task_manager, 1872 ) 1873 1874 def generation( 1875 self, 1876 *, 1877 id: typing.Optional[str] = None, 1878 trace_id: typing.Optional[str] = None, 1879 parent_observation_id: typing.Optional[str] = None, 1880 name: typing.Optional[str] = None, 1881 start_time: typing.Optional[dt.datetime] = None, 1882 end_time: typing.Optional[dt.datetime] = None, 1883 completion_start_time: typing.Optional[dt.datetime] = None, 1884 metadata: typing.Optional[typing.Any] = None, 1885 level: typing.Optional[SpanLevel] = None, 1886 status_message: typing.Optional[str] = None, 1887 version: typing.Optional[str] = None, 1888 model: typing.Optional[str] = None, 1889 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 1890 input: typing.Optional[typing.Any] = None, 1891 output: typing.Optional[typing.Any] = None, 1892 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 1893 usage_details: typing.Optional[typing.Dict[str, int]] = None, 1894 cost_details: typing.Optional[typing.Dict[str, float]] = None, 1895 prompt: typing.Optional[PromptClient] = None, 1896 **kwargs, 1897 ) -> "StatefulGenerationClient": 1898 """Create a generation. 1899 1900 A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI. 1901 1902 Usually, you want to add a generation nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id. 1903 1904 If no trace_id is provided, a new trace is created just for this generation. 1905 1906 Args: 1907 id (Optional[str]): The id of the generation can be set, defaults to random id. 1908 trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created 1909 parent_observation_id (Optional[str]): The ID of the parent observation, if applicable. 1910 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 1911 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 1912 end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`. 1913 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 1914 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 1915 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 1916 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 1917 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 1918 model (Optional[str]): The name of the model used for the generation. 1919 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 1920 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 1921 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 1922 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 1923 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 1924 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 1925 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 1926 **kwargs: Additional keyword arguments to include in the generation. 1927 1928 Returns: 1929 StatefulGenerationClient: The created generation. 1930 1931 Example: 1932 ```python 1933 from langfuse import Langfuse 1934 1935 langfuse = Langfuse() 1936 1937 # Create a generation in Langfuse 1938 generation = langfuse.generation( 1939 name="summary-generation", 1940 model="gpt-3.5-turbo", 1941 model_parameters={"maxTokens": "1000", "temperature": "0.9"}, 1942 input=[{"role": "system", "content": "You are a helpful assistant."}, 1943 {"role": "user", "content": "Please generate a summary of the following documents ..."}], 1944 metadata={"interface": "whatsapp"} 1945 ) 1946 ``` 1947 """ 1948 new_trace_id = trace_id or str(uuid.uuid4()) 1949 new_generation_id = id or str(uuid.uuid4()) 1950 self.trace_id = new_trace_id 1951 try: 1952 generation_body = { 1953 "id": new_generation_id, 1954 "trace_id": new_trace_id, 1955 "release": self.release, 1956 "name": name, 1957 "start_time": start_time or _get_timestamp(), 1958 "metadata": metadata, 1959 "input": input, 1960 "output": output, 1961 "level": level, 1962 "status_message": status_message, 1963 "parent_observation_id": parent_observation_id, 1964 "version": version, 1965 "end_time": end_time, 1966 "completion_start_time": completion_start_time, 1967 "model": model, 1968 "model_parameters": model_parameters, 1969 "usage": _convert_usage_input(usage) if usage is not None else None, 1970 "usage_details": usage_details, 1971 "cost_details": cost_details, 1972 "trace": {"release": self.release}, 1973 **_create_prompt_context(prompt), 1974 **kwargs, 1975 } 1976 1977 if trace_id is None: 1978 trace = { 1979 "id": new_trace_id, 1980 "release": self.release, 1981 "name": name, 1982 } 1983 request = TraceBody(**trace) 1984 1985 event = { 1986 "id": str(uuid.uuid4()), 1987 "type": "trace-create", 1988 "body": request, 1989 } 1990 1991 self.log.debug("Creating trace...") 1992 1993 self.task_manager.add_task(event) 1994 1995 self.log.debug( 1996 f"Creating generation max {_filter_io_from_event_body(generation_body)}..." 1997 ) 1998 request = CreateGenerationBody(**generation_body) 1999 2000 event = { 2001 "id": str(uuid.uuid4()), 2002 "type": "generation-create", 2003 "body": request, 2004 } 2005 2006 self.task_manager.add_task(event) 2007 2008 except Exception as e: 2009 self.log.exception(e) 2010 finally: 2011 return StatefulGenerationClient( 2012 self.client, 2013 new_generation_id, 2014 StateType.OBSERVATION, 2015 new_trace_id, 2016 self.task_manager, 2017 ) 2018 2019 def _generate_trace(self, trace_id: str, name: str): 2020 trace_dict = { 2021 "id": trace_id, 2022 "release": self.release, 2023 "name": name, 2024 } 2025 2026 trace_body = TraceBody(**trace_dict) 2027 2028 event = { 2029 "id": str(uuid.uuid4()), 2030 "type": "trace-create", 2031 "body": trace_body, 2032 } 2033 2034 self.log.debug(f"Creating trace {_filter_io_from_event_body(trace_dict)}...") 2035 self.task_manager.add_task(event) 2036 2037 def join(self): 2038 """Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter. 2039 2040 If called before flushing, consumers might terminate before sending all events to Langfuse API. This method is called at exit of the SKD, right before the Python interpreter closes. 2041 To guarantee all messages have been delivered, you still need to call flush(). 2042 """ 2043 try: 2044 return self.task_manager.join() 2045 except Exception as e: 2046 self.log.exception(e) 2047 2048 def flush(self): 2049 """Flush the internal event queue to the Langfuse API. It blocks until the queue is empty. It should be called when the application shuts down. 2050 2051 Example: 2052 ```python 2053 from langfuse import Langfuse 2054 2055 langfuse = Langfuse() 2056 2057 # Some operations with Langfuse 2058 2059 # Flushing all events to end Langfuse cleanly 2060 langfuse.flush() 2061 ``` 2062 """ 2063 try: 2064 return self.task_manager.flush() 2065 except Exception as e: 2066 self.log.exception(e) 2067 2068 def shutdown(self): 2069 """Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated. 2070 2071 This function calls flush() and join() consecutively resulting in a complete shutdown of the SDK. On success of this function, no more events will be sent to Langfuse API. 2072 As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API. 2073 """ 2074 try: 2075 self.prompt_cache._task_manager.shutdown() 2076 2077 # In logging.py, a handler is attached to the httpx logger. 2078 # To avoid a memory leak on singleton reset, remove all handlers 2079 httpx_logger = logging.getLogger("httpx") 2080 for handler in httpx_logger.handlers: 2081 httpx_logger.removeHandler(handler) 2082 2083 return self.task_manager.shutdown() 2084 except Exception as e: 2085 self.log.exception(e)
Langfuse Python client.
Attributes:
- log (logging.Logger): Logger for the Langfuse client.
- base_url (str): Base URL of the Langfuse API, serving as the root address for API endpoint construction.
- httpx_client (httpx.Client): HTTPX client utilized for executing requests to the Langfuse API.
- client (FernLangfuse): Core interface for Langfuse API interaction.
- task_manager (TaskManager): Task Manager dedicated to handling asynchronous tasks.
- release (str): Identifies the release number or hash of the application.
- prompt_cache (PromptCache): A cache for efficiently storing and retrieving PromptClient instances.
Example:
Initiating the Langfuse client should always be first step to use Langfuse.
import os from langfuse import Langfuse # Set the public and secret keys as environment variables os.environ['LANGFUSE_PUBLIC_KEY'] = public_key os.environ['LANGFUSE_SECRET_KEY'] = secret_key # Initialize the Langfuse client using the credentials langfuse = Langfuse()
172 def __init__( 173 self, 174 public_key: Optional[str] = None, 175 secret_key: Optional[str] = None, 176 host: Optional[str] = None, 177 release: Optional[str] = None, 178 debug: bool = False, 179 threads: Optional[int] = None, 180 flush_at: Optional[int] = None, 181 flush_interval: Optional[float] = None, 182 max_retries: Optional[int] = None, 183 timeout: Optional[int] = None, # seconds 184 sdk_integration: Optional[str] = "default", 185 httpx_client: Optional[httpx.Client] = None, 186 enabled: Optional[bool] = True, 187 sample_rate: Optional[float] = None, 188 mask: Optional[MaskFunction] = None, 189 ): 190 """Initialize the Langfuse client. 191 192 Args: 193 public_key: Public API key of Langfuse project. Can be set via `LANGFUSE_PUBLIC_KEY` environment variable. 194 secret_key: Secret API key of Langfuse project. Can be set via `LANGFUSE_SECRET_KEY` environment variable. 195 host: Host of Langfuse API. Can be set via `LANGFUSE_HOST` environment variable. Defaults to `https://cloud.langfuse.com`. 196 release: Release number/hash of the application to provide analytics grouped by release. Can be set via `LANGFUSE_RELEASE` environment variable. 197 debug: Enables debug mode for more verbose logging. Can be set via `LANGFUSE_DEBUG` environment variable. 198 threads: Number of consumer threads to execute network requests. Helps scaling the SDK for high load. Only increase this if you run into scaling issues. 199 flush_at: Max batch size that's sent to the API. 200 flush_interval: Max delay until a new batch is sent to the API. 201 max_retries: Max number of retries in case of API/network errors. 202 timeout: Timeout of API requests in seconds. Defaults to 20 seconds. 203 httpx_client: Pass your own httpx client for more customizability of requests. 204 sdk_integration: Used by intgerations that wrap the Langfuse SDK to add context for debugging and support. Not to be used directly. 205 enabled: Enables or disables the Langfuse client. If disabled, all observability calls to the backend will be no-ops. 206 sample_rate: Sampling rate for tracing. If set to 0.2, only 20% of the data will be sent to the backend. Can be set via `LANGFUSE_SAMPLE_RATE` environment variable. 207 mask (langfuse.types.MaskFunction): Masking function for 'input' and 'output' fields in events. Function must take a single keyword argument `data` and return a serializable, masked version of the data. 208 209 Raises: 210 ValueError: If public_key or secret_key are not set and not found in environment variables. 211 212 Example: 213 Initiating the Langfuse client should always be first step to use Langfuse. 214 ```python 215 import os 216 from langfuse import Langfuse 217 218 # Set the public and secret keys as environment variables 219 os.environ['LANGFUSE_PUBLIC_KEY'] = public_key 220 os.environ['LANGFUSE_SECRET_KEY'] = secret_key 221 222 # Initialize the Langfuse client using the credentials 223 langfuse = Langfuse() 224 ``` 225 """ 226 self.enabled = enabled 227 public_key = public_key or os.environ.get("LANGFUSE_PUBLIC_KEY") 228 secret_key = secret_key or os.environ.get("LANGFUSE_SECRET_KEY") 229 sample_rate = ( 230 sample_rate 231 if sample_rate 232 is not None # needs explicit None check, as 0 is a valid value 233 else float(os.environ.get("LANGFUSE_SAMPLE_RATE", 1.0)) 234 ) 235 236 if sample_rate is not None and ( 237 sample_rate > 1 or sample_rate < 0 238 ): # default value 1 will be set in the taskmanager 239 self.enabled = False 240 self.log.warning( 241 "Langfuse client is disabled since the sample rate provided is not between 0 and 1." 242 ) 243 244 threads = threads or int(os.environ.get("LANGFUSE_THREADS", 1)) 245 flush_at = flush_at or int(os.environ.get("LANGFUSE_FLUSH_AT", 15)) 246 flush_interval = flush_interval or float( 247 os.environ.get("LANGFUSE_FLUSH_INTERVAL", 0.5) 248 ) 249 250 max_retries = max_retries or int(os.environ.get("LANGFUSE_MAX_RETRIES", 3)) 251 timeout = timeout or int(os.environ.get("LANGFUSE_TIMEOUT", 20)) 252 253 if not self.enabled: 254 self.log.warning( 255 "Langfuse client is disabled. No observability data will be sent." 256 ) 257 258 elif not public_key: 259 self.enabled = False 260 self.log.warning( 261 "Langfuse client is disabled since no public_key was provided as a parameter or environment variable 'LANGFUSE_PUBLIC_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client" 262 ) 263 264 elif not secret_key: 265 self.enabled = False 266 self.log.warning( 267 "Langfuse client is disabled since no secret_key was provided as a parameter or environment variable 'LANGFUSE_SECRET_KEY'. See our docs: https://langfuse.com/docs/sdk/python/low-level-sdk#initialize-client" 268 ) 269 270 set_debug = debug if debug else (os.getenv("LANGFUSE_DEBUG", "False") == "True") 271 272 if set_debug is True: 273 # Ensures that debug level messages are logged when debug mode is on. 274 # Otherwise, defaults to WARNING level. 275 # See https://docs.python.org/3/howto/logging.html#what-happens-if-no-configuration-is-provided 276 logging.basicConfig() 277 # Set level for all loggers under langfuse package 278 logging.getLogger("langfuse").setLevel(logging.DEBUG) 279 280 clean_logger() 281 else: 282 logging.getLogger("langfuse").setLevel(logging.WARNING) 283 clean_logger() 284 285 self.base_url = ( 286 host 287 if host 288 else os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com") 289 ) 290 291 self.httpx_client = httpx_client or httpx.Client(timeout=timeout) 292 293 public_api_client = FernLangfuse( 294 base_url=self.base_url, 295 username=public_key, 296 password=secret_key, 297 x_langfuse_sdk_name="python", 298 x_langfuse_sdk_version=version, 299 x_langfuse_public_key=public_key, 300 httpx_client=self.httpx_client, 301 timeout=timeout, 302 ) 303 async_public_api_client = AsyncFernLangfuse( 304 base_url=self.base_url, 305 username=public_key, 306 password=secret_key, 307 x_langfuse_sdk_name="python", 308 x_langfuse_sdk_version=version, 309 x_langfuse_public_key=public_key, 310 timeout=timeout, 311 ) 312 313 self.api = public_api_client 314 self.client = public_api_client # legacy, to be removed in next major release 315 self.async_api = async_public_api_client 316 317 langfuse_client = LangfuseClient( 318 public_key=public_key, 319 secret_key=secret_key, 320 base_url=self.base_url, 321 version=version, 322 timeout=timeout, 323 session=self.httpx_client, 324 ) 325 326 args = { 327 "threads": threads, 328 "flush_at": flush_at, 329 "flush_interval": flush_interval, 330 "max_retries": max_retries, 331 "client": langfuse_client, 332 "api_client": self.client, 333 "public_key": public_key, 334 "sdk_name": "python", 335 "sdk_version": version, 336 "sdk_integration": sdk_integration, 337 "enabled": self.enabled, 338 "sample_rate": sample_rate, 339 "mask": mask, 340 } 341 342 self.task_manager = TaskManager(**args) 343 344 self.trace_id = None 345 self.project_id = None 346 347 self.release = self._get_release_value(release) 348 349 self.prompt_cache = PromptCache()
Initialize the Langfuse client.
Arguments:
- public_key: Public API key of Langfuse project. Can be set via
LANGFUSE_PUBLIC_KEY
environment variable. - secret_key: Secret API key of Langfuse project. Can be set via
LANGFUSE_SECRET_KEY
environment variable. - host: Host of Langfuse API. Can be set via
LANGFUSE_HOST
environment variable. Defaults tohttps://cloud.langfuse.com
. - release: Release number/hash of the application to provide analytics grouped by release. Can be set via
LANGFUSE_RELEASE
environment variable. - debug: Enables debug mode for more verbose logging. Can be set via
LANGFUSE_DEBUG
environment variable. - threads: Number of consumer threads to execute network requests. Helps scaling the SDK for high load. Only increase this if you run into scaling issues.
- flush_at: Max batch size that's sent to the API.
- flush_interval: Max delay until a new batch is sent to the API.
- max_retries: Max number of retries in case of API/network errors.
- timeout: Timeout of API requests in seconds. Defaults to 20 seconds.
- httpx_client: Pass your own httpx client for more customizability of requests.
- sdk_integration: Used by intgerations that wrap the Langfuse SDK to add context for debugging and support. Not to be used directly.
- enabled: Enables or disables the Langfuse client. If disabled, all observability calls to the backend will be no-ops.
- sample_rate: Sampling rate for tracing. If set to 0.2, only 20% of the data will be sent to the backend. Can be set via
LANGFUSE_SAMPLE_RATE
environment variable. - mask (langfuse.types.MaskFunction): Masking function for 'input' and 'output' fields in events. Function must take a single keyword argument
data
and return a serializable, masked version of the data.
Raises:
- ValueError: If public_key or secret_key are not set and not found in environment variables.
Example:
Initiating the Langfuse client should always be first step to use Langfuse.
import os from langfuse import Langfuse # Set the public and secret keys as environment variables os.environ['LANGFUSE_PUBLIC_KEY'] = public_key os.environ['LANGFUSE_SECRET_KEY'] = secret_key # Initialize the Langfuse client using the credentials langfuse = Langfuse()
374 def get_trace_url(self) -> str: 375 """Get the URL of the current trace to view it in the Langfuse UI.""" 376 project_id = self._get_project_id() 377 if not project_id: 378 return f"{self.base_url}/trace/{self.trace_id}" 379 380 return f"{self.base_url}/project/{project_id}/traces/{self.trace_id}"
Get the URL of the current trace to view it in the Langfuse UI.
382 def get_dataset( 383 self, name: str, *, fetch_items_page_size: Optional[int] = 50 384 ) -> "DatasetClient": 385 """Fetch a dataset by its name. 386 387 Args: 388 name (str): The name of the dataset to fetch. 389 fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50. 390 391 Returns: 392 DatasetClient: The dataset with the given name. 393 """ 394 try: 395 self.log.debug(f"Getting datasets {name}") 396 dataset = self.client.datasets.get(dataset_name=name) 397 398 dataset_items = [] 399 page = 1 400 while True: 401 new_items = self.client.dataset_items.list( 402 dataset_name=self._url_encode(name), 403 page=page, 404 limit=fetch_items_page_size, 405 ) 406 dataset_items.extend(new_items.data) 407 if new_items.meta.total_pages <= page: 408 break 409 page += 1 410 411 items = [DatasetItemClient(i, langfuse=self) for i in dataset_items] 412 413 return DatasetClient(dataset, items=items) 414 except Exception as e: 415 handle_fern_exception(e) 416 raise e
Fetch a dataset by its name.
Arguments:
- name (str): The name of the dataset to fetch.
- fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
Returns:
DatasetClient: The dataset with the given name.
418 def get_dataset_item(self, id: str) -> "DatasetItemClient": 419 """Get the dataset item with the given id.""" 420 try: 421 self.log.debug(f"Getting dataset item {id}") 422 dataset_item = self.client.dataset_items.get(id=id) 423 return DatasetItemClient(dataset_item, langfuse=self) 424 except Exception as e: 425 handle_fern_exception(e) 426 raise e
Get the dataset item with the given id.
428 def auth_check(self) -> bool: 429 """Check if the provided credentials (public and secret key) are valid. 430 431 Raises: 432 Exception: If no projects were found for the provided credentials. 433 434 Note: 435 This method is blocking. It is discouraged to use it in production code. 436 """ 437 try: 438 projects = self.client.projects.get() 439 self.log.debug( 440 f"Auth check successful, found {len(projects.data)} projects" 441 ) 442 if len(projects.data) == 0: 443 raise Exception( 444 "Auth check failed, no project found for the keys provided." 445 ) 446 return True 447 448 except Exception as e: 449 handle_fern_exception(e) 450 raise e
Check if the provided credentials (public and secret key) are valid.
Raises:
- Exception: If no projects were found for the provided credentials.
Note:
This method is blocking. It is discouraged to use it in production code.
452 def get_dataset_runs( 453 self, 454 dataset_name: str, 455 *, 456 page: typing.Optional[int] = None, 457 limit: typing.Optional[int] = None, 458 ) -> PaginatedDatasetRuns: 459 """Get all dataset runs. 460 461 Args: 462 dataset_name (str): Name of the dataset. 463 page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None. 464 limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50. 465 466 Returns: 467 PaginatedDatasetRuns: The dataset runs. 468 """ 469 try: 470 self.log.debug("Getting dataset runs") 471 return self.client.datasets.get_runs( 472 dataset_name=self._url_encode(dataset_name), page=page, limit=limit 473 ) 474 except Exception as e: 475 handle_fern_exception(e) 476 raise e
Get all dataset runs.
Arguments:
- dataset_name (str): Name of the dataset.
- page (Optional[int]): Page number of the dataset runs to return, starts at 1. Defaults to None.
- limit (Optional[int]): Maximum number of dataset runs to return. Defaults to 50.
Returns:
PaginatedDatasetRuns: The dataset runs.
478 def get_dataset_run( 479 self, 480 dataset_name: str, 481 dataset_run_name: str, 482 ) -> DatasetRunWithItems: 483 """Get a dataset run. 484 485 Args: 486 dataset_name: Name of the dataset. 487 dataset_run_name: Name of the dataset run. 488 489 Returns: 490 DatasetRunWithItems: The dataset run. 491 """ 492 try: 493 self.log.debug( 494 f"Getting dataset runs for dataset {dataset_name} and run {dataset_run_name}" 495 ) 496 return self.client.datasets.get_run( 497 dataset_name=self._url_encode(dataset_name), 498 run_name=self._url_encode(dataset_run_name), 499 ) 500 except Exception as e: 501 handle_fern_exception(e) 502 raise e
Get a dataset run.
Arguments:
- dataset_name: Name of the dataset.
- dataset_run_name: Name of the dataset run.
Returns:
DatasetRunWithItems: The dataset run.
504 def create_dataset( 505 self, 506 name: str, 507 description: Optional[str] = None, 508 metadata: Optional[Any] = None, 509 ) -> Dataset: 510 """Create a dataset with the given name on Langfuse. 511 512 Args: 513 name: Name of the dataset to create. 514 description: Description of the dataset. Defaults to None. 515 metadata: Additional metadata. Defaults to None. 516 517 Returns: 518 Dataset: The created dataset as returned by the Langfuse API. 519 """ 520 try: 521 body = CreateDatasetRequest( 522 name=name, description=description, metadata=metadata 523 ) 524 self.log.debug(f"Creating datasets {body}") 525 return self.client.datasets.create(request=body) 526 except Exception as e: 527 handle_fern_exception(e) 528 raise e
Create a dataset with the given name on Langfuse.
Arguments:
- name: Name of the dataset to create.
- description: Description of the dataset. Defaults to None.
- metadata: Additional metadata. Defaults to None.
Returns:
Dataset: The created dataset as returned by the Langfuse API.
530 def create_dataset_item( 531 self, 532 dataset_name: str, 533 input: Optional[Any] = None, 534 expected_output: Optional[Any] = None, 535 metadata: Optional[Any] = None, 536 source_trace_id: Optional[str] = None, 537 source_observation_id: Optional[str] = None, 538 status: Optional[DatasetStatus] = None, 539 id: Optional[str] = None, 540 ) -> DatasetItem: 541 """Create a dataset item. 542 543 Upserts if an item with id already exists. 544 545 Args: 546 dataset_name: Name of the dataset in which the dataset item should be created. 547 input: Input data. Defaults to None. Can contain any dict, list or scalar. 548 expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar. 549 metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar. 550 source_trace_id: Id of the source trace. Defaults to None. 551 source_observation_id: Id of the source observation. Defaults to None. 552 status: Status of the dataset item. Defaults to ACTIVE for newly created items. 553 id: Id of the dataset item. Defaults to None. Provide your own id if you want to dedupe dataset items. Id needs to be globally unique and cannot be reused across datasets. 554 555 Returns: 556 DatasetItem: The created dataset item as returned by the Langfuse API. 557 558 Example: 559 ```python 560 from langfuse import Langfuse 561 562 langfuse = Langfuse() 563 564 # Uploading items to the Langfuse dataset named "capital_cities" 565 langfuse.create_dataset_item( 566 dataset_name="capital_cities", 567 input={"input": {"country": "Italy"}}, 568 expected_output={"expected_output": "Rome"}, 569 metadata={"foo": "bar"} 570 ) 571 ``` 572 """ 573 try: 574 body = CreateDatasetItemRequest( 575 datasetName=dataset_name, 576 input=input, 577 expectedOutput=expected_output, 578 metadata=metadata, 579 sourceTraceId=source_trace_id, 580 sourceObservationId=source_observation_id, 581 status=status, 582 id=id, 583 ) 584 self.log.debug(f"Creating dataset item {body}") 585 return self.client.dataset_items.create(request=body) 586 except Exception as e: 587 handle_fern_exception(e) 588 raise e
Create a dataset item.
Upserts if an item with id already exists.
Arguments:
- dataset_name: Name of the dataset in which the dataset item should be created.
- input: Input data. Defaults to None. Can contain any dict, list or scalar.
- expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
- metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
- source_trace_id: Id of the source trace. Defaults to None.
- source_observation_id: Id of the source observation. Defaults to None.
- status: Status of the dataset item. Defaults to ACTIVE for newly created items.
- id: Id of the dataset item. Defaults to None. Provide your own id if you want to dedupe dataset items. Id needs to be globally unique and cannot be reused across datasets.
Returns:
DatasetItem: The created dataset item as returned by the Langfuse API.
Example:
from langfuse import Langfuse langfuse = Langfuse() # Uploading items to the Langfuse dataset named "capital_cities" langfuse.create_dataset_item( dataset_name="capital_cities", input={"input": {"country": "Italy"}}, expected_output={"expected_output": "Rome"}, metadata={"foo": "bar"} )
590 def fetch_trace( 591 self, 592 id: str, 593 ) -> FetchTraceResponse: 594 """Fetch a trace via the Langfuse API by its id. 595 596 Args: 597 id: The id of the trace to fetch. 598 599 Returns: 600 FetchTraceResponse: The trace with full details as returned by the Langfuse API on `data`. 601 602 Raises: 603 Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request. 604 """ 605 try: 606 self.log.debug(f"Getting trace {id}") 607 trace = self.client.trace.get(id) 608 return FetchTraceResponse(data=trace) 609 except Exception as e: 610 handle_fern_exception(e) 611 raise e
Fetch a trace via the Langfuse API by its id.
Arguments:
- id: The id of the trace to fetch.
Returns:
FetchTraceResponse: The trace with full details as returned by the Langfuse API on
data
.
Raises:
- Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
613 def get_trace( 614 self, 615 id: str, 616 ) -> TraceWithFullDetails: 617 """Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead. 618 619 Args: 620 id: The id of the trace to fetch. 621 622 Returns: 623 TraceWithFullDetails: The trace with full details as returned by the Langfuse API. 624 625 Raises: 626 Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request. 627 """ 628 warnings.warn( 629 "get_trace is deprecated, use fetch_trace instead.", 630 DeprecationWarning, 631 ) 632 633 try: 634 self.log.debug(f"Getting trace {id}") 635 return self.client.trace.get(id) 636 except Exception as e: 637 handle_fern_exception(e) 638 raise e
Get a trace via the Langfuse API by its id. Deprecated, use fetch_trace instead.
Arguments:
- id: The id of the trace to fetch.
Returns:
TraceWithFullDetails: The trace with full details as returned by the Langfuse API.
Raises:
- Exception: If the trace with the given id could not be found within the authenticated project or if an error occurred during the request.
640 def fetch_traces( 641 self, 642 *, 643 page: Optional[int] = None, 644 limit: Optional[int] = None, 645 user_id: Optional[str] = None, 646 name: Optional[str] = None, 647 session_id: Optional[str] = None, 648 from_timestamp: Optional[dt.datetime] = None, 649 to_timestamp: Optional[dt.datetime] = None, 650 order_by: Optional[str] = None, 651 tags: Optional[Union[str, Sequence[str]]] = None, 652 ) -> FetchTracesResponse: 653 """Fetch a list of traces in the current project matching the given parameters. 654 655 Args: 656 page (Optional[int]): Page number, starts at 1. Defaults to None. 657 limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None. 658 name (Optional[str]): Filter by name of traces. Defaults to None. 659 user_id (Optional[str]): Filter by user_id. Defaults to None. 660 session_id (Optional[str]): Filter by session_id. Defaults to None. 661 from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None. 662 to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None. 663 order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None. 664 tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None. 665 666 Returns: 667 FetchTracesResponse, list of traces on `data` and metadata on `meta`. 668 669 Raises: 670 Exception: If an error occurred during the request. 671 """ 672 try: 673 self.log.debug( 674 f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}" 675 ) 676 res = self.client.trace.list( 677 page=page, 678 limit=limit, 679 name=name, 680 user_id=user_id, 681 session_id=session_id, 682 from_timestamp=from_timestamp, 683 to_timestamp=to_timestamp, 684 order_by=order_by, 685 tags=tags, 686 ) 687 return FetchTracesResponse(data=res.data, meta=res.meta) 688 except Exception as e: 689 handle_fern_exception(e) 690 raise e
Fetch a list of traces in the current project matching the given parameters.
Arguments:
- page (Optional[int]): Page number, starts at 1. Defaults to None.
- limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
- name (Optional[str]): Filter by name of traces. Defaults to None.
- user_id (Optional[str]): Filter by user_id. Defaults to None.
- session_id (Optional[str]): Filter by session_id. Defaults to None.
- from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
- to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
- order_by (Optional[str]): Format of the string
[field].[asc/desc]
. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example:timestamp.asc
. Defaults to None. - tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
Returns:
FetchTracesResponse, list of traces on
data
and metadata onmeta
.
Raises:
- Exception: If an error occurred during the request.
692 def get_traces( 693 self, 694 *, 695 page: Optional[int] = None, 696 limit: Optional[int] = None, 697 user_id: Optional[str] = None, 698 name: Optional[str] = None, 699 session_id: Optional[str] = None, 700 from_timestamp: Optional[dt.datetime] = None, 701 to_timestamp: Optional[dt.datetime] = None, 702 order_by: Optional[str] = None, 703 tags: Optional[Union[str, Sequence[str]]] = None, 704 ) -> Traces: 705 """Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead. 706 707 Args: 708 page (Optional[int]): Page number, starts at 1. Defaults to None. 709 limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None. 710 name (Optional[str]): Filter by name of traces. Defaults to None. 711 user_id (Optional[str]): Filter by user_id. Defaults to None. 712 session_id (Optional[str]): Filter by session_id. Defaults to None. 713 from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None. 714 to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None. 715 order_by (Optional[str]): Format of the string `[field].[asc/desc]`. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example: `timestamp.asc`. Defaults to None. 716 tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None. 717 718 Returns: 719 List of Traces 720 721 Raises: 722 Exception: If an error occurred during the request. 723 """ 724 warnings.warn( 725 "get_traces is deprecated, use fetch_traces instead.", 726 DeprecationWarning, 727 ) 728 try: 729 self.log.debug( 730 f"Getting traces... {page}, {limit}, {name}, {user_id}, {session_id}, {from_timestamp}, {to_timestamp}, {order_by}, {tags}" 731 ) 732 return self.client.trace.list( 733 page=page, 734 limit=limit, 735 name=name, 736 user_id=user_id, 737 session_id=session_id, 738 from_timestamp=from_timestamp, 739 to_timestamp=to_timestamp, 740 order_by=order_by, 741 tags=tags, 742 ) 743 except Exception as e: 744 handle_fern_exception(e) 745 raise e
Get a list of traces in the current project matching the given parameters. Deprecated, use fetch_traces instead.
Arguments:
- page (Optional[int]): Page number, starts at 1. Defaults to None.
- limit (Optional[int]): Limit of items per page. If you encounter API issues due to too large page sizes, try to reduce the limit. Defaults to None.
- name (Optional[str]): Filter by name of traces. Defaults to None.
- user_id (Optional[str]): Filter by user_id. Defaults to None.
- session_id (Optional[str]): Filter by session_id. Defaults to None.
- from_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp on or after this datetime. Defaults to None.
- to_timestamp (Optional[dt.datetime]): Retrieve only traces with a timestamp before this datetime. Defaults to None.
- order_by (Optional[str]): Format of the string
[field].[asc/desc]
. Fields: id, timestamp, name, userId, release, version, public, bookmarked, sessionId. Example:timestamp.asc
. Defaults to None. - tags (Optional[Union[str, Sequence[str]]]): Filter by tags. Defaults to None.
Returns:
List of Traces
Raises:
- Exception: If an error occurred during the request.
747 def fetch_observations( 748 self, 749 *, 750 page: typing.Optional[int] = None, 751 limit: typing.Optional[int] = None, 752 name: typing.Optional[str] = None, 753 user_id: typing.Optional[str] = None, 754 trace_id: typing.Optional[str] = None, 755 parent_observation_id: typing.Optional[str] = None, 756 from_start_time: typing.Optional[dt.datetime] = None, 757 to_start_time: typing.Optional[dt.datetime] = None, 758 type: typing.Optional[str] = None, 759 ) -> FetchObservationsResponse: 760 """Get a list of observations in the current project matching the given parameters. 761 762 Args: 763 page (Optional[int]): Page number of the observations to return. Defaults to None. 764 limit (Optional[int]): Maximum number of observations to return. Defaults to None. 765 name (Optional[str]): Name of the observations to return. Defaults to None. 766 user_id (Optional[str]): User identifier. Defaults to None. 767 trace_id (Optional[str]): Trace identifier. Defaults to None. 768 parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None. 769 from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None. 770 to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None. 771 type (Optional[str]): Type of the observation. Defaults to None. 772 773 Returns: 774 FetchObservationsResponse, list of observations on `data` and metadata on `meta`. 775 776 Raises: 777 Exception: If an error occurred during the request. 778 """ 779 try: 780 self.log.debug( 781 f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}" 782 ) 783 res = self.client.observations.get_many( 784 page=page, 785 limit=limit, 786 name=name, 787 user_id=user_id, 788 trace_id=trace_id, 789 parent_observation_id=parent_observation_id, 790 from_start_time=from_start_time, 791 to_start_time=to_start_time, 792 type=type, 793 ) 794 return FetchObservationsResponse(data=res.data, meta=res.meta) 795 except Exception as e: 796 self.log.exception(e) 797 raise e
Get a list of observations in the current project matching the given parameters.
Arguments:
- page (Optional[int]): Page number of the observations to return. Defaults to None.
- limit (Optional[int]): Maximum number of observations to return. Defaults to None.
- name (Optional[str]): Name of the observations to return. Defaults to None.
- user_id (Optional[str]): User identifier. Defaults to None.
- trace_id (Optional[str]): Trace identifier. Defaults to None.
- parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
- from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
- to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
- type (Optional[str]): Type of the observation. Defaults to None.
Returns:
FetchObservationsResponse, list of observations on
data
and metadata onmeta
.
Raises:
- Exception: If an error occurred during the request.
799 def get_observations( 800 self, 801 *, 802 page: typing.Optional[int] = None, 803 limit: typing.Optional[int] = None, 804 name: typing.Optional[str] = None, 805 user_id: typing.Optional[str] = None, 806 trace_id: typing.Optional[str] = None, 807 parent_observation_id: typing.Optional[str] = None, 808 from_start_time: typing.Optional[dt.datetime] = None, 809 to_start_time: typing.Optional[dt.datetime] = None, 810 type: typing.Optional[str] = None, 811 ) -> ObservationsViews: 812 """Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead. 813 814 Args: 815 page (Optional[int]): Page number of the observations to return. Defaults to None. 816 limit (Optional[int]): Maximum number of observations to return. Defaults to None. 817 name (Optional[str]): Name of the observations to return. Defaults to None. 818 user_id (Optional[str]): User identifier. Defaults to None. 819 trace_id (Optional[str]): Trace identifier. Defaults to None. 820 parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None. 821 from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None. 822 to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None. 823 type (Optional[str]): Type of the observation. Defaults to None. 824 825 Returns: 826 List of ObservationsViews: List of observations in the project matching the given parameters. 827 828 Raises: 829 Exception: If an error occurred during the request. 830 """ 831 warnings.warn( 832 "get_observations is deprecated, use fetch_observations instead.", 833 DeprecationWarning, 834 ) 835 try: 836 self.log.debug( 837 f"Getting observations... {page}, {limit}, {name}, {user_id}, {trace_id}, {parent_observation_id}, {from_start_time}, {to_start_time}, {type}" 838 ) 839 return self.client.observations.get_many( 840 page=page, 841 limit=limit, 842 name=name, 843 user_id=user_id, 844 trace_id=trace_id, 845 parent_observation_id=parent_observation_id, 846 from_start_time=from_start_time, 847 to_start_time=to_start_time, 848 type=type, 849 ) 850 except Exception as e: 851 handle_fern_exception(e) 852 raise e
Get a list of observations in the current project matching the given parameters. Deprecated, use fetch_observations instead.
Arguments:
- page (Optional[int]): Page number of the observations to return. Defaults to None.
- limit (Optional[int]): Maximum number of observations to return. Defaults to None.
- name (Optional[str]): Name of the observations to return. Defaults to None.
- user_id (Optional[str]): User identifier. Defaults to None.
- trace_id (Optional[str]): Trace identifier. Defaults to None.
- parent_observation_id (Optional[str]): Parent observation identifier. Defaults to None.
- from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
- to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
- type (Optional[str]): Type of the observation. Defaults to None.
Returns:
List of ObservationsViews: List of observations in the project matching the given parameters.
Raises:
- Exception: If an error occurred during the request.
854 def get_generations( 855 self, 856 *, 857 page: typing.Optional[int] = None, 858 limit: typing.Optional[int] = None, 859 name: typing.Optional[str] = None, 860 user_id: typing.Optional[str] = None, 861 trace_id: typing.Optional[str] = None, 862 from_start_time: typing.Optional[dt.datetime] = None, 863 to_start_time: typing.Optional[dt.datetime] = None, 864 parent_observation_id: typing.Optional[str] = None, 865 ) -> ObservationsViews: 866 """Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead. 867 868 Args: 869 page (Optional[int]): Page number of the generations to return. Defaults to None. 870 limit (Optional[int]): Maximum number of generations to return. Defaults to None. 871 name (Optional[str]): Name of the generations to return. Defaults to None. 872 user_id (Optional[str]): User identifier of the generations to return. Defaults to None. 873 trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None. 874 from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None. 875 to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None. 876 parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None. 877 878 Returns: 879 List of ObservationsViews: List of generations in the project matching the given parameters. 880 881 Raises: 882 Exception: If an error occurred during the request. 883 """ 884 warnings.warn( 885 "get_generations is deprecated, use `fetch_observations(type='GENERATION')` instead.", 886 DeprecationWarning, 887 ) 888 return self.get_observations( 889 page=page, 890 limit=limit, 891 name=name, 892 user_id=user_id, 893 trace_id=trace_id, 894 parent_observation_id=parent_observation_id, 895 from_start_time=from_start_time, 896 to_start_time=to_start_time, 897 type="GENERATION", 898 )
Get a list of generations in the current project matching the given parameters. Deprecated, use fetch_observations(type='GENERATION') instead.
Arguments:
- page (Optional[int]): Page number of the generations to return. Defaults to None.
- limit (Optional[int]): Maximum number of generations to return. Defaults to None.
- name (Optional[str]): Name of the generations to return. Defaults to None.
- user_id (Optional[str]): User identifier of the generations to return. Defaults to None.
- trace_id (Optional[str]): Trace identifier of the generations to return. Defaults to None.
- from_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time on or after this datetime. Defaults to None.
- to_start_time (Optional[dt.datetime]): Retrieve only observations with a start_time before this datetime. Defaults to None.
- parent_observation_id (Optional[str]): Parent observation identifier of the generations to return. Defaults to None.
Returns:
List of ObservationsViews: List of generations in the project matching the given parameters.
Raises:
- Exception: If an error occurred during the request.
900 def fetch_observation( 901 self, 902 id: str, 903 ) -> FetchObservationResponse: 904 """Get an observation in the current project with the given identifier. 905 906 Args: 907 id: The identifier of the observation to fetch. 908 909 Returns: 910 FetchObservationResponse: The observation with the given id on `data`. 911 912 Raises: 913 Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request. 914 """ 915 try: 916 self.log.debug(f"Getting observation {id}") 917 observation = self.client.observations.get(id) 918 return FetchObservationResponse(data=observation) 919 except Exception as e: 920 handle_fern_exception(e) 921 raise e
Get an observation in the current project with the given identifier.
Arguments:
- id: The identifier of the observation to fetch.
Returns:
FetchObservationResponse: The observation with the given id on
data
.
Raises:
- Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
923 def fetch_media(self, id: str) -> FetchMediaResponse: 924 """Get media content by ID. 925 926 Args: 927 id: The identifier of the media content to fetch. 928 929 Returns: 930 FetchMediaResponse: The media data of the given id on `data`. 931 932 Raises: 933 Exception: If the media content with the given id could not be found within the authenticated project or if an error occurred during the request. 934 """ 935 try: 936 return FetchMediaResponse(data=self.client.media.get(id)) 937 except Exception as e: 938 handle_fern_exception(e) 939 raise e
Get media content by ID.
Arguments:
- id: The identifier of the media content to fetch.
Returns:
FetchMediaResponse: The media data of the given id on
data
.
Raises:
- Exception: If the media content with the given id could not be found within the authenticated project or if an error occurred during the request.
941 def resolve_media_references( 942 self, 943 *, 944 obj: Any, 945 resolve_with: Literal["base64_data_uri"], 946 max_depth: int = 10, 947 content_fetch_timeout_seconds: int = 10, 948 ): 949 """Replace media reference strings in an object with base64 data URIs. 950 951 This method recursively traverses an object (up to max_depth) looking for media reference strings 952 in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using 953 the provided Langfuse client and replaces the reference string with a base64 data URI. 954 955 If fetching media content fails for a reference string, a warning is logged and the reference 956 string is left unchanged. 957 958 Args: 959 obj: The object to process. Can be a primitive value, array, or nested object. 960 If the object has a __dict__ attribute, a dict will be returned instead of the original object type. 961 resolve_with: The representation of the media content to replace the media reference string with. 962 Currently only "base64_data_uri" is supported. 963 max_depth: int: The maximum depth to traverse the object. Default is 10. 964 content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 10. 965 966 Returns: 967 A deep copy of the input object with all media references replaced with base64 data URIs where possible. 968 If the input object has a __dict__ attribute, a dict will be returned instead of the original object type. 969 970 Example: 971 obj = { 972 "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@", 973 "nested": { 974 "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@" 975 } 976 } 977 978 result = await LangfuseMedia.resolve_media_references(obj, langfuse_client) 979 980 # Result: 981 # { 982 # "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", 983 # "nested": { 984 # "pdf": "data:application/pdf;base64,JVBERi0xLjcK..." 985 # } 986 # } 987 """ 988 return LangfuseMedia.resolve_media_references( 989 langfuse_client=self, 990 obj=obj, 991 resolve_with=resolve_with, 992 max_depth=max_depth, 993 content_fetch_timeout_seconds=content_fetch_timeout_seconds, 994 )
Replace media reference strings in an object with base64 data URIs.
This method recursively traverses an object (up to max_depth) looking for media reference strings in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using the provided Langfuse client and replaces the reference string with a base64 data URI.
If fetching media content fails for a reference string, a warning is logged and the reference string is left unchanged.
Arguments:
- obj: The object to process. Can be a primitive value, array, or nested object. If the object has a __dict__ attribute, a dict will be returned instead of the original object type.
- resolve_with: The representation of the media content to replace the media reference string with. Currently only "base64_data_uri" is supported.
- max_depth: int: The maximum depth to traverse the object. Default is 10.
- content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 10.
Returns:
A deep copy of the input object with all media references replaced with base64 data URIs where possible. If the input object has a __dict__ attribute, a dict will be returned instead of the original object type.
Example:
obj = { "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@", "nested": { "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@" } }
result = await LangfuseMedia.resolve_media_references(obj, langfuse_client)
Result:
{
"image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"nested": {
"pdf": "data:application/pdf;base64,JVBERi0xLjcK..."
}
}
996 def get_observation( 997 self, 998 id: str, 999 ) -> Observation: 1000 """Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead. 1001 1002 Args: 1003 id: The identifier of the observation to fetch. 1004 1005 Raises: 1006 Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request. 1007 """ 1008 warnings.warn( 1009 "get_observation is deprecated, use fetch_observation instead.", 1010 DeprecationWarning, 1011 ) 1012 try: 1013 self.log.debug(f"Getting observation {id}") 1014 return self.client.observations.get(id) 1015 except Exception as e: 1016 handle_fern_exception(e) 1017 raise e
Get an observation in the current project with the given identifier. Deprecated, use fetch_observation instead.
Arguments:
- id: The identifier of the observation to fetch.
Raises:
- Exception: If the observation with the given id could not be found within the authenticated project or if an error occurred during the request.
1019 def fetch_sessions( 1020 self, 1021 *, 1022 page: typing.Optional[int] = None, 1023 limit: typing.Optional[int] = None, 1024 from_timestamp: typing.Optional[dt.datetime] = None, 1025 to_timestamp: typing.Optional[dt.datetime] = None, 1026 ) -> FetchSessionsResponse: 1027 """Get a list of sessions in the current project. 1028 1029 Args: 1030 page (Optional[int]): Page number of the sessions to return. Defaults to None. 1031 limit (Optional[int]): Maximum number of sessions to return. Defaults to None. 1032 from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None. 1033 to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None. 1034 1035 Returns: 1036 FetchSessionsResponse, list of sessions on `data` and metadata on `meta`. 1037 1038 Raises: 1039 Exception: If an error occurred during the request. 1040 """ 1041 try: 1042 self.log.debug( 1043 f"Getting sessions... {page}, {limit}, {from_timestamp}, {to_timestamp}" 1044 ) 1045 res = self.client.sessions.list( 1046 page=page, 1047 limit=limit, 1048 from_timestamp=from_timestamp, 1049 to_timestamp=to_timestamp, 1050 ) 1051 return FetchSessionsResponse(data=res.data, meta=res.meta) 1052 except Exception as e: 1053 handle_fern_exception(e) 1054 raise e
Get a list of sessions in the current project.
Arguments:
- page (Optional[int]): Page number of the sessions to return. Defaults to None.
- limit (Optional[int]): Maximum number of sessions to return. Defaults to None.
- from_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp on or after this datetime. Defaults to None.
- to_timestamp (Optional[dt.datetime]): Retrieve only sessions with a timestamp before this datetime. Defaults to None.
Returns:
FetchSessionsResponse, list of sessions on
data
and metadata onmeta
.
Raises:
- Exception: If an error occurred during the request.
1084 def get_prompt( 1085 self, 1086 name: str, 1087 version: Optional[int] = None, 1088 *, 1089 label: Optional[str] = None, 1090 type: Literal["chat", "text"] = "text", 1091 cache_ttl_seconds: Optional[int] = None, 1092 fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None, 1093 max_retries: Optional[int] = None, 1094 fetch_timeout_seconds: Optional[int] = None, 1095 ) -> PromptClient: 1096 """Get a prompt. 1097 1098 This method attempts to fetch the requested prompt from the local cache. If the prompt is not found 1099 in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again 1100 and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will 1101 return the expired prompt as a fallback. 1102 1103 Args: 1104 name (str): The name of the prompt to retrieve. 1105 1106 Keyword Args: 1107 version (Optional[int]): The version of the prompt to retrieve. If no label and version is specified, the `production` label is returned. Specify either version or label, not both. 1108 label: Optional[str]: The label of the prompt to retrieve. If no label and version is specified, the `production` label is returned. Specify either version or label, not both. 1109 cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a 1110 keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0. 1111 type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text". 1112 fallback: Union[Optional[List[ChatMessageDict]], Optional[str]]: The prompt string to return if fetching the prompt fails. Important on the first call where no cached prompt is available. Follows Langfuse prompt formatting with double curly braces for variables. Defaults to None. 1113 max_retries: Optional[int]: The maximum number of retries in case of API/network errors. Defaults to 2. The maximum value is 4. Retries have an exponential backoff with a maximum delay of 10 seconds. 1114 fetch_timeout_seconds: Optional[int]: The timeout in milliseconds for fetching the prompt. Defaults to the default timeout set on the SDK, which is 10 seconds per default. 1115 1116 Returns: 1117 The prompt object retrieved from the cache or directly fetched if not cached or expired of type 1118 - TextPromptClient, if type argument is 'text'. 1119 - ChatPromptClient, if type argument is 'chat'. 1120 1121 Raises: 1122 Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an 1123 expired prompt in the cache, in which case it logs a warning and returns the expired prompt. 1124 """ 1125 if version is not None and label is not None: 1126 raise ValueError("Cannot specify both version and label at the same time.") 1127 1128 if not name: 1129 raise ValueError("Prompt name cannot be empty.") 1130 1131 cache_key = PromptCache.generate_cache_key(name, version=version, label=label) 1132 bounded_max_retries = self._get_bounded_max_retries( 1133 max_retries, default_max_retries=2, max_retries_upper_bound=4 1134 ) 1135 1136 self.log.debug(f"Getting prompt '{cache_key}'") 1137 cached_prompt = self.prompt_cache.get(cache_key) 1138 1139 if cached_prompt is None or cache_ttl_seconds == 0: 1140 self.log.debug( 1141 f"Prompt '{cache_key}' not found in cache or caching disabled." 1142 ) 1143 try: 1144 return self._fetch_prompt_and_update_cache( 1145 name, 1146 version=version, 1147 label=label, 1148 ttl_seconds=cache_ttl_seconds, 1149 max_retries=bounded_max_retries, 1150 fetch_timeout_seconds=fetch_timeout_seconds, 1151 ) 1152 except Exception as e: 1153 if fallback: 1154 self.log.warning( 1155 f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}" 1156 ) 1157 1158 fallback_client_args = { 1159 "name": name, 1160 "prompt": fallback, 1161 "type": type, 1162 "version": version or 0, 1163 "config": {}, 1164 "labels": [label] if label else [], 1165 "tags": [], 1166 } 1167 1168 if type == "text": 1169 return TextPromptClient( 1170 prompt=Prompt_Text(**fallback_client_args), 1171 is_fallback=True, 1172 ) 1173 1174 if type == "chat": 1175 return ChatPromptClient( 1176 prompt=Prompt_Chat(**fallback_client_args), 1177 is_fallback=True, 1178 ) 1179 1180 raise e 1181 1182 if cached_prompt.is_expired(): 1183 self.log.debug(f"Stale prompt '{cache_key}' found in cache.") 1184 try: 1185 # refresh prompt in background thread, refresh_prompt deduplicates tasks 1186 self.log.debug(f"Refreshing prompt '{cache_key}' in background.") 1187 self.prompt_cache.add_refresh_prompt_task( 1188 cache_key, 1189 lambda: self._fetch_prompt_and_update_cache( 1190 name, 1191 version=version, 1192 label=label, 1193 ttl_seconds=cache_ttl_seconds, 1194 max_retries=bounded_max_retries, 1195 fetch_timeout_seconds=fetch_timeout_seconds, 1196 ), 1197 ) 1198 self.log.debug(f"Returning stale prompt '{cache_key}' from cache.") 1199 # return stale prompt 1200 return cached_prompt.value 1201 1202 except Exception as e: 1203 self.log.warning( 1204 f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}" 1205 ) 1206 # creation of refresh prompt task failed, return stale prompt 1207 return cached_prompt.value 1208 1209 return cached_prompt.value
Get a prompt.
This method attempts to fetch the requested prompt from the local cache. If the prompt is not found in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will return the expired prompt as a fallback.
Arguments:
- name (str): The name of the prompt to retrieve.
Keyword Args:
version (Optional[int]): The version of the prompt to retrieve. If no label and version is specified, the
production
label is returned. Specify either version or label, not both. label: Optional[str]: The label of the prompt to retrieve. If no label and version is specified, theproduction
label is returned. Specify either version or label, not both. cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0. type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text". fallback: Union[Optional[List[ChatMessageDict]], Optional[str]]: The prompt string to return if fetching the prompt fails. Important on the first call where no cached prompt is available. Follows Langfuse prompt formatting with double curly braces for variables. Defaults to None. max_retries: Optional[int]: The maximum number of retries in case of API/network errors. Defaults to 2. The maximum value is 4. Retries have an exponential backoff with a maximum delay of 10 seconds. fetch_timeout_seconds: Optional[int]: The timeout in milliseconds for fetching the prompt. Defaults to the default timeout set on the SDK, which is 10 seconds per default.
Returns:
The prompt object retrieved from the cache or directly fetched if not cached or expired of type
- TextPromptClient, if type argument is 'text'.
- ChatPromptClient, if type argument is 'chat'.
Raises:
- Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
- expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
1303 def create_prompt( 1304 self, 1305 *, 1306 name: str, 1307 prompt: Union[str, List[ChatMessageDict]], 1308 is_active: Optional[bool] = None, # deprecated 1309 labels: List[str] = [], 1310 tags: Optional[List[str]] = None, 1311 type: Optional[Literal["chat", "text"]] = "text", 1312 config: Optional[Any] = None, 1313 commit_message: Optional[str] = None, 1314 ) -> PromptClient: 1315 """Create a new prompt in Langfuse. 1316 1317 Keyword Args: 1318 name : The name of the prompt to be created. 1319 prompt : The content of the prompt to be created. 1320 is_active [DEPRECATED] : A flag indicating whether the prompt is active or not. This is deprecated and will be removed in a future release. Please use the 'production' label instead. 1321 labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label. 1322 tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt. 1323 config: Additional structured data to be saved with the prompt. Defaults to None. 1324 type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text". 1325 commit_message: Optional string describing the change. 1326 1327 Returns: 1328 TextPromptClient: The prompt if type argument is 'text'. 1329 ChatPromptClient: The prompt if type argument is 'chat'. 1330 """ 1331 try: 1332 self.log.debug(f"Creating prompt {name=}, {version=}, {labels=}") 1333 1334 # Handle deprecated is_active flag 1335 if is_active: 1336 self.log.warning( 1337 "The 'is_active' flag is deprecated and will be removed in a future release. Please use the 'production' label instead." 1338 ) 1339 1340 labels = labels if "production" in labels else labels + ["production"] 1341 1342 if type == "chat": 1343 if not isinstance(prompt, list): 1344 raise ValueError( 1345 "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes." 1346 ) 1347 request = CreatePromptRequest_Chat( 1348 name=name, 1349 prompt=prompt, 1350 labels=labels, 1351 tags=tags, 1352 config=config or {}, 1353 commitMessage=commit_message, 1354 type="chat", 1355 ) 1356 server_prompt = self.client.prompts.create(request=request) 1357 1358 return ChatPromptClient(prompt=server_prompt) 1359 1360 if not isinstance(prompt, str): 1361 raise ValueError("For 'text' type, 'prompt' must be a string.") 1362 1363 request = CreatePromptRequest_Text( 1364 name=name, 1365 prompt=prompt, 1366 labels=labels, 1367 tags=tags, 1368 config=config or {}, 1369 commitMessage=commit_message, 1370 type="text", 1371 ) 1372 1373 server_prompt = self.client.prompts.create(request=request) 1374 return TextPromptClient(prompt=server_prompt) 1375 1376 except Exception as e: 1377 handle_fern_exception(e) 1378 raise e
Create a new prompt in Langfuse.
Keyword Args:
name : The name of the prompt to be created. prompt : The content of the prompt to be created. is_active [DEPRECATED] : A flag indicating whether the prompt is active or not. This is deprecated and will be removed in a future release. Please use the 'production' label instead. labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label. tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt. config: Additional structured data to be saved with the prompt. Defaults to None. type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text". commit_message: Optional string describing the change.
Returns:
TextPromptClient: The prompt if type argument is 'text'. ChatPromptClient: The prompt if type argument is 'chat'.
1380 def update_prompt( 1381 self, 1382 *, 1383 name: str, 1384 version: int, 1385 new_labels: List[str] = [], 1386 ): 1387 """Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name. 1388 1389 Args: 1390 name (str): The name of the prompt to update. 1391 version (int): The version number of the prompt to update. 1392 new_labels (List[str], optional): New labels to assign to the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. Defaults to []. 1393 1394 Returns: 1395 Prompt: The updated prompt from the Langfuse API. 1396 1397 """ 1398 updated_prompt = self.client.prompt_version.update( 1399 name=name, 1400 version=version, 1401 new_labels=new_labels, 1402 ) 1403 self.prompt_cache.invalidate(name) 1404 return updated_prompt
Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.
Arguments:
- name (str): The name of the prompt to update.
- version (int): The version number of the prompt to update.
- new_labels (List[str], optional): New labels to assign to the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. Defaults to [].
Returns:
Prompt: The updated prompt from the Langfuse API.
1409 def trace( 1410 self, 1411 *, 1412 id: typing.Optional[str] = None, 1413 name: typing.Optional[str] = None, 1414 user_id: typing.Optional[str] = None, 1415 session_id: typing.Optional[str] = None, 1416 version: typing.Optional[str] = None, 1417 input: typing.Optional[typing.Any] = None, 1418 output: typing.Optional[typing.Any] = None, 1419 metadata: typing.Optional[typing.Any] = None, 1420 tags: typing.Optional[typing.List[str]] = None, 1421 timestamp: typing.Optional[dt.datetime] = None, 1422 public: typing.Optional[bool] = None, 1423 **kwargs, 1424 ) -> "StatefulTraceClient": 1425 """Create a trace. 1426 1427 Args: 1428 id: The id of the trace can be set, defaults to a random id. Set it to link traces to external systems or when creating a distributed trace. Traces are upserted on id. 1429 name: Identifier of the trace. Useful for sorting/filtering in the UI. 1430 input: The input of the trace. Can be any JSON object. 1431 output: The output of the trace. Can be any JSON object. 1432 metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API. 1433 user_id: The id of the user that triggered the execution. Used to provide user-level analytics. 1434 session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier. 1435 version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging. 1436 release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging. 1437 tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API. 1438 timestamp: The timestamp of the trace. Defaults to the current time if not provided. 1439 public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project. 1440 **kwargs: Additional keyword arguments that can be included in the trace. 1441 1442 Returns: 1443 StatefulTraceClient: The created trace. 1444 1445 Example: 1446 ```python 1447 from langfuse import Langfuse 1448 1449 langfuse = Langfuse() 1450 1451 trace = langfuse.trace( 1452 name="example-application", 1453 user_id="user-1234") 1454 ) 1455 ``` 1456 """ 1457 new_id = id or str(uuid.uuid4()) 1458 self.trace_id = new_id 1459 try: 1460 new_dict = { 1461 "id": new_id, 1462 "name": name, 1463 "userId": user_id, 1464 "sessionId": session_id 1465 or kwargs.get("sessionId", None), # backward compatibility 1466 "release": self.release, 1467 "version": version, 1468 "metadata": metadata, 1469 "input": input, 1470 "output": output, 1471 "tags": tags, 1472 "timestamp": timestamp or _get_timestamp(), 1473 "public": public, 1474 } 1475 if kwargs is not None: 1476 new_dict.update(kwargs) 1477 1478 new_body = TraceBody(**new_dict) 1479 1480 self.log.debug(f"Creating trace {_filter_io_from_event_body(new_dict)}") 1481 event = { 1482 "id": str(uuid.uuid4()), 1483 "type": "trace-create", 1484 "body": new_body, 1485 } 1486 1487 self.task_manager.add_task( 1488 event, 1489 ) 1490 1491 except Exception as e: 1492 self.log.exception(e) 1493 finally: 1494 self._log_memory_usage() 1495 1496 return StatefulTraceClient( 1497 self.client, new_id, StateType.TRACE, new_id, self.task_manager 1498 )
Create a trace.
Arguments:
- id: The id of the trace can be set, defaults to a random id. Set it to link traces to external systems or when creating a distributed trace. Traces are upserted on id.
- name: Identifier of the trace. Useful for sorting/filtering in the UI.
- input: The input of the trace. Can be any JSON object.
- output: The output of the trace. Can be any JSON object.
- metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
- user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
- session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
- version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
- release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
- tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
- timestamp: The timestamp of the trace. Defaults to the current time if not provided.
- public: You can make a trace
public
to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project. - **kwargs: Additional keyword arguments that can be included in the trace.
Returns:
StatefulTraceClient: The created trace.
Example:
from langfuse import Langfuse langfuse = Langfuse() trace = langfuse.trace( name="example-application", user_id="user-1234") )
1577 def score( 1578 self, 1579 *, 1580 name: str, 1581 value: typing.Union[float, str], 1582 data_type: typing.Optional[ScoreDataType] = None, 1583 trace_id: typing.Optional[str] = None, 1584 id: typing.Optional[str] = None, 1585 comment: typing.Optional[str] = None, 1586 observation_id: typing.Optional[str] = None, 1587 config_id: typing.Optional[str] = None, 1588 **kwargs, 1589 ) -> "StatefulClient": 1590 """Create a score attached to a trace (and optionally an observation). 1591 1592 Args: 1593 name (str): Identifier of the score. 1594 value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores. 1595 data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. 1596 When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores. 1597 trace_id (str): The id of the trace to which the score should be attached. 1598 id (Optional[str]): The id of the score. If not provided, a new UUID is generated. 1599 comment (Optional[str]): Additional context/explanation of the score. 1600 observation_id (Optional[str]): The id of the observation to which the score should be attached. 1601 config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None. 1602 **kwargs: Additional keyword arguments to include in the score. 1603 1604 Returns: 1605 StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided). 1606 1607 Example: 1608 ```python 1609 from langfuse import Langfuse 1610 1611 langfuse = Langfuse() 1612 1613 # Create a trace 1614 trace = langfuse.trace(name="example-application") 1615 1616 # Get id of created trace 1617 trace_id = trace.id 1618 1619 # Add score to the trace 1620 trace = langfuse.score( 1621 trace_id=trace_id, 1622 name="user-explicit-feedback", 1623 value=0.9, 1624 comment="I like how personalized the response is" 1625 ) 1626 ``` 1627 """ 1628 trace_id = trace_id or self.trace_id or str(uuid.uuid4()) 1629 new_id = id or str(uuid.uuid4()) 1630 try: 1631 new_dict = { 1632 "id": new_id, 1633 "trace_id": trace_id, 1634 "observation_id": observation_id, 1635 "name": name, 1636 "value": value, 1637 "data_type": data_type, 1638 "comment": comment, 1639 "config_id": config_id, 1640 **kwargs, 1641 } 1642 1643 self.log.debug(f"Creating score {new_dict}...") 1644 new_body = ScoreBody(**new_dict) 1645 1646 event = { 1647 "id": str(uuid.uuid4()), 1648 "type": "score-create", 1649 "body": new_body, 1650 } 1651 self.task_manager.add_task(event) 1652 1653 except Exception as e: 1654 self.log.exception(e) 1655 finally: 1656 if observation_id is not None: 1657 return StatefulClient( 1658 self.client, 1659 observation_id, 1660 StateType.OBSERVATION, 1661 trace_id, 1662 self.task_manager, 1663 ) 1664 else: 1665 return StatefulClient( 1666 self.client, new_id, StateType.TRACE, new_id, self.task_manager 1667 )
Create a score attached to a trace (and optionally an observation).
Arguments:
- name (str): Identifier of the score.
- value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
- data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
- trace_id (str): The id of the trace to which the score should be attached.
- id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
- comment (Optional[str]): Additional context/explanation of the score.
- observation_id (Optional[str]): The id of the observation to which the score should be attached.
- config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
- **kwargs: Additional keyword arguments to include in the score.
Returns:
StatefulClient: Either the associated observation (if observation_id is provided) or the trace (if observation_id is not provided).
Example:
from langfuse import Langfuse langfuse = Langfuse() # Create a trace trace = langfuse.trace(name="example-application") # Get id of created trace trace_id = trace.id # Add score to the trace trace = langfuse.score( trace_id=trace_id, name="user-explicit-feedback", value=0.9, comment="I like how personalized the response is" )
1669 def span( 1670 self, 1671 *, 1672 id: typing.Optional[str] = None, 1673 trace_id: typing.Optional[str] = None, 1674 parent_observation_id: typing.Optional[str] = None, 1675 name: typing.Optional[str] = None, 1676 start_time: typing.Optional[dt.datetime] = None, 1677 end_time: typing.Optional[dt.datetime] = None, 1678 metadata: typing.Optional[typing.Any] = None, 1679 level: typing.Optional[SpanLevel] = None, 1680 status_message: typing.Optional[str] = None, 1681 input: typing.Optional[typing.Any] = None, 1682 output: typing.Optional[typing.Any] = None, 1683 version: typing.Optional[str] = None, 1684 **kwargs, 1685 ) -> "StatefulSpanClient": 1686 """Create a span. 1687 1688 A span represents durations of units of work in a trace. 1689 Usually, you want to add a span nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id. 1690 1691 If no trace_id is provided, a new trace is created just for this span. 1692 1693 Args: 1694 id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id. 1695 trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated. 1696 parent_observation_id (Optional[str]): The ID of the parent observation, if applicable. 1697 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 1698 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 1699 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 1700 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 1701 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 1702 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 1703 input (Optional[dict]): The input to the span. Can be any JSON object. 1704 output (Optional[dict]): The output to the span. Can be any JSON object. 1705 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 1706 **kwargs: Additional keyword arguments to include in the span. 1707 1708 Returns: 1709 StatefulSpanClient: The created span. 1710 1711 Example: 1712 ```python 1713 from langfuse import Langfuse 1714 1715 langfuse = Langfuse() 1716 1717 trace = langfuse.trace(name = "llm-feature") 1718 1719 # Create a span 1720 retrieval = langfuse.span(name = "retrieval", trace_id = trace.id) 1721 1722 # Create a nested span 1723 nested_span = langfuse.span(name = "retrieval", trace_id = trace.id, parent_observation_id = retrieval.id) 1724 ``` 1725 """ 1726 new_span_id = id or str(uuid.uuid4()) 1727 new_trace_id = trace_id or str(uuid.uuid4()) 1728 self.trace_id = new_trace_id 1729 try: 1730 span_body = { 1731 "id": new_span_id, 1732 "trace_id": new_trace_id, 1733 "name": name, 1734 "start_time": start_time or _get_timestamp(), 1735 "metadata": metadata, 1736 "input": input, 1737 "output": output, 1738 "level": level, 1739 "status_message": status_message, 1740 "parent_observation_id": parent_observation_id, 1741 "version": version, 1742 "end_time": end_time, 1743 "trace": {"release": self.release}, 1744 **kwargs, 1745 } 1746 1747 if trace_id is None: 1748 self._generate_trace(new_trace_id, name or new_trace_id) 1749 1750 self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...") 1751 1752 span_body = CreateSpanBody(**span_body) 1753 1754 event = { 1755 "id": str(uuid.uuid4()), 1756 "type": "span-create", 1757 "body": span_body, 1758 } 1759 1760 self.task_manager.add_task(event) 1761 1762 except Exception as e: 1763 self.log.exception(e) 1764 finally: 1765 self._log_memory_usage() 1766 1767 return StatefulSpanClient( 1768 self.client, 1769 new_span_id, 1770 StateType.OBSERVATION, 1771 new_trace_id, 1772 self.task_manager, 1773 )
Create a span.
A span represents durations of units of work in a trace. Usually, you want to add a span nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
If no trace_id is provided, a new trace is created just for this span.
Arguments:
- id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
- trace_id (Optional[str]): The trace ID associated with this span. If not provided, a new UUID is generated.
- parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
- name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
- end_time (Optional[datetime]): The time at which the span ended. Automatically set by
span.end()
. - metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
- level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. 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)
1775 def event( 1776 self, 1777 *, 1778 id: typing.Optional[str] = None, 1779 trace_id: typing.Optional[str] = None, 1780 parent_observation_id: typing.Optional[str] = None, 1781 name: typing.Optional[str] = None, 1782 start_time: typing.Optional[dt.datetime] = None, 1783 metadata: typing.Optional[typing.Any] = None, 1784 input: typing.Optional[typing.Any] = None, 1785 output: typing.Optional[typing.Any] = None, 1786 level: typing.Optional[SpanLevel] = None, 1787 status_message: typing.Optional[str] = None, 1788 version: typing.Optional[str] = None, 1789 **kwargs, 1790 ) -> "StatefulSpanClient": 1791 """Create an event. 1792 1793 An event represents a discrete event in a trace. 1794 Usually, you want to add a event nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id. 1795 1796 If no trace_id is provided, a new trace is created just for this event. 1797 1798 Args: 1799 id (Optional[str]): The id of the event can be set, otherwise a random id is generated. 1800 trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event. 1801 parent_observation_id (Optional[str]): The ID of the parent observation, if applicable. 1802 name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI. 1803 start_time (Optional[datetime]): The time at which the event started, defaults to the current time. 1804 metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API. 1805 input (Optional[Any]): The input to the event. Can be any JSON object. 1806 output (Optional[Any]): The output to the event. Can be any JSON object. 1807 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 1808 status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event. 1809 version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging. 1810 **kwargs: Additional keyword arguments to include in the event. 1811 1812 Returns: 1813 StatefulSpanClient: The created event. 1814 1815 Example: 1816 ```python 1817 from langfuse import Langfuse 1818 1819 langfuse = Langfuse() 1820 1821 trace = langfuse.trace(name = "llm-feature") 1822 1823 # Create an event 1824 retrieval = langfuse.event(name = "retrieval", trace_id = trace.id) 1825 ``` 1826 """ 1827 event_id = id or str(uuid.uuid4()) 1828 new_trace_id = trace_id or str(uuid.uuid4()) 1829 self.trace_id = new_trace_id 1830 try: 1831 event_body = { 1832 "id": event_id, 1833 "trace_id": new_trace_id, 1834 "name": name, 1835 "start_time": start_time or _get_timestamp(), 1836 "metadata": metadata, 1837 "input": input, 1838 "output": output, 1839 "level": level, 1840 "status_message": status_message, 1841 "parent_observation_id": parent_observation_id, 1842 "version": version, 1843 "trace": {"release": self.release}, 1844 **kwargs, 1845 } 1846 1847 if trace_id is None: 1848 self._generate_trace(new_trace_id, name or new_trace_id) 1849 1850 request = CreateEventBody(**event_body) 1851 1852 event = { 1853 "id": str(uuid.uuid4()), 1854 "type": "event-create", 1855 "body": request, 1856 } 1857 1858 self.log.debug( 1859 f"Creating event {_filter_io_from_event_body(event_body)} ..." 1860 ) 1861 self.task_manager.add_task(event) 1862 1863 except Exception as e: 1864 self.log.exception(e) 1865 finally: 1866 return StatefulSpanClient( 1867 self.client, 1868 event_id, 1869 StateType.OBSERVATION, 1870 new_trace_id, 1871 self.task_manager, 1872 )
Create an event.
An event represents a discrete event in a trace. Usually, you want to add a event nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
If no trace_id is provided, a new trace is created just for this event.
Arguments:
- id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
- trace_id (Optional[str]): The trace ID associated with this event. If not provided, a new trace is created just for this event.
- parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
- name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
- metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
- input (Optional[Any]): The input to the event. Can be any JSON object.
- output (Optional[Any]): The output to the event. Can be any JSON object.
- level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. 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)
1874 def generation( 1875 self, 1876 *, 1877 id: typing.Optional[str] = None, 1878 trace_id: typing.Optional[str] = None, 1879 parent_observation_id: typing.Optional[str] = None, 1880 name: typing.Optional[str] = None, 1881 start_time: typing.Optional[dt.datetime] = None, 1882 end_time: typing.Optional[dt.datetime] = None, 1883 completion_start_time: typing.Optional[dt.datetime] = None, 1884 metadata: typing.Optional[typing.Any] = None, 1885 level: typing.Optional[SpanLevel] = None, 1886 status_message: typing.Optional[str] = None, 1887 version: typing.Optional[str] = None, 1888 model: typing.Optional[str] = None, 1889 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 1890 input: typing.Optional[typing.Any] = None, 1891 output: typing.Optional[typing.Any] = None, 1892 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 1893 usage_details: typing.Optional[typing.Dict[str, int]] = None, 1894 cost_details: typing.Optional[typing.Dict[str, float]] = None, 1895 prompt: typing.Optional[PromptClient] = None, 1896 **kwargs, 1897 ) -> "StatefulGenerationClient": 1898 """Create a generation. 1899 1900 A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI. 1901 1902 Usually, you want to add a generation nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id. 1903 1904 If no trace_id is provided, a new trace is created just for this generation. 1905 1906 Args: 1907 id (Optional[str]): The id of the generation can be set, defaults to random id. 1908 trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created 1909 parent_observation_id (Optional[str]): The ID of the parent observation, if applicable. 1910 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 1911 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 1912 end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`. 1913 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 1914 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 1915 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 1916 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 1917 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 1918 model (Optional[str]): The name of the model used for the generation. 1919 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 1920 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 1921 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 1922 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 1923 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 1924 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 1925 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 1926 **kwargs: Additional keyword arguments to include in the generation. 1927 1928 Returns: 1929 StatefulGenerationClient: The created generation. 1930 1931 Example: 1932 ```python 1933 from langfuse import Langfuse 1934 1935 langfuse = Langfuse() 1936 1937 # Create a generation in Langfuse 1938 generation = langfuse.generation( 1939 name="summary-generation", 1940 model="gpt-3.5-turbo", 1941 model_parameters={"maxTokens": "1000", "temperature": "0.9"}, 1942 input=[{"role": "system", "content": "You are a helpful assistant."}, 1943 {"role": "user", "content": "Please generate a summary of the following documents ..."}], 1944 metadata={"interface": "whatsapp"} 1945 ) 1946 ``` 1947 """ 1948 new_trace_id = trace_id or str(uuid.uuid4()) 1949 new_generation_id = id or str(uuid.uuid4()) 1950 self.trace_id = new_trace_id 1951 try: 1952 generation_body = { 1953 "id": new_generation_id, 1954 "trace_id": new_trace_id, 1955 "release": self.release, 1956 "name": name, 1957 "start_time": start_time or _get_timestamp(), 1958 "metadata": metadata, 1959 "input": input, 1960 "output": output, 1961 "level": level, 1962 "status_message": status_message, 1963 "parent_observation_id": parent_observation_id, 1964 "version": version, 1965 "end_time": end_time, 1966 "completion_start_time": completion_start_time, 1967 "model": model, 1968 "model_parameters": model_parameters, 1969 "usage": _convert_usage_input(usage) if usage is not None else None, 1970 "usage_details": usage_details, 1971 "cost_details": cost_details, 1972 "trace": {"release": self.release}, 1973 **_create_prompt_context(prompt), 1974 **kwargs, 1975 } 1976 1977 if trace_id is None: 1978 trace = { 1979 "id": new_trace_id, 1980 "release": self.release, 1981 "name": name, 1982 } 1983 request = TraceBody(**trace) 1984 1985 event = { 1986 "id": str(uuid.uuid4()), 1987 "type": "trace-create", 1988 "body": request, 1989 } 1990 1991 self.log.debug("Creating trace...") 1992 1993 self.task_manager.add_task(event) 1994 1995 self.log.debug( 1996 f"Creating generation max {_filter_io_from_event_body(generation_body)}..." 1997 ) 1998 request = CreateGenerationBody(**generation_body) 1999 2000 event = { 2001 "id": str(uuid.uuid4()), 2002 "type": "generation-create", 2003 "body": request, 2004 } 2005 2006 self.task_manager.add_task(event) 2007 2008 except Exception as e: 2009 self.log.exception(e) 2010 finally: 2011 return StatefulGenerationClient( 2012 self.client, 2013 new_generation_id, 2014 StateType.OBSERVATION, 2015 new_trace_id, 2016 self.task_manager, 2017 )
Create a generation.
A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.
Usually, you want to add a generation nested within a trace. Optionally you can nest it within another observation by providing a parent_observation_id.
If no trace_id is provided, a new trace is created just for this generation.
Arguments:
- id (Optional[str]): The id of the generation can be set, defaults to random id.
- trace_id (Optional[str]): The trace ID associated with this generation. If not provided, a new trace is created
- parent_observation_id (Optional[str]): The ID of the parent observation, if applicable.
- name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
- end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by
generation.end()
. - completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
- metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
- level (Optional[str]): The level of the generation. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. - status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
- version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
- model (Optional[str]): The name of the model used for the generation.
- model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
- input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
- output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
- usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {
promptTokens
,completionTokens
,totalTokens
} and a more generic version {input
,output
,total
,unit
,inputCost
,outputCost
,totalCost
} where unit can be of value"TOKENS"
,"CHARACTERS"
,"MILLISECONDS"
,"SECONDS"
, or"IMAGES"
. Refer to the docs on how to automatically infer token usage and costs in Langfuse. - usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
- cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
- prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
- **kwargs: Additional keyword arguments to include in the generation.
Returns:
StatefulGenerationClient: The created generation.
Example:
from langfuse import Langfuse langfuse = Langfuse() # Create a generation in Langfuse generation = langfuse.generation( name="summary-generation", model="gpt-3.5-turbo", model_parameters={"maxTokens": "1000", "temperature": "0.9"}, input=[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Please generate a summary of the following documents ..."}], metadata={"interface": "whatsapp"} )
2037 def join(self): 2038 """Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter. 2039 2040 If called before flushing, consumers might terminate before sending all events to Langfuse API. This method is called at exit of the SKD, right before the Python interpreter closes. 2041 To guarantee all messages have been delivered, you still need to call flush(). 2042 """ 2043 try: 2044 return self.task_manager.join() 2045 except Exception as e: 2046 self.log.exception(e)
Blocks until all consumer Threads are terminated. The SKD calls this upon termination of the Python Interpreter.
If called before flushing, consumers might terminate before sending all events to Langfuse API. This method is called at exit of the SKD, right before the Python interpreter closes. To guarantee all messages have been delivered, you still need to call flush().
2048 def flush(self): 2049 """Flush the internal event queue to the Langfuse API. It blocks until the queue is empty. It should be called when the application shuts down. 2050 2051 Example: 2052 ```python 2053 from langfuse import Langfuse 2054 2055 langfuse = Langfuse() 2056 2057 # Some operations with Langfuse 2058 2059 # Flushing all events to end Langfuse cleanly 2060 langfuse.flush() 2061 ``` 2062 """ 2063 try: 2064 return self.task_manager.flush() 2065 except Exception as e: 2066 self.log.exception(e)
Flush the internal event queue to the Langfuse API. It blocks until the queue is empty. It should be called when the application shuts down.
Example:
from langfuse import Langfuse langfuse = Langfuse() # Some operations with Langfuse # Flushing all events to end Langfuse cleanly langfuse.flush()
2068 def shutdown(self): 2069 """Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated. 2070 2071 This function calls flush() and join() consecutively resulting in a complete shutdown of the SDK. On success of this function, no more events will be sent to Langfuse API. 2072 As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API. 2073 """ 2074 try: 2075 self.prompt_cache._task_manager.shutdown() 2076 2077 # In logging.py, a handler is attached to the httpx logger. 2078 # To avoid a memory leak on singleton reset, remove all handlers 2079 httpx_logger = logging.getLogger("httpx") 2080 for handler in httpx_logger.handlers: 2081 httpx_logger.removeHandler(handler) 2082 2083 return self.task_manager.shutdown() 2084 except Exception as e: 2085 self.log.exception(e)
Initiate a graceful shutdown of the Langfuse SDK, ensuring all events are sent to Langfuse API and all consumer Threads are terminated.
This function calls flush() and join() consecutively resulting in a complete shutdown of the SDK. On success of this function, no more events will be sent to Langfuse API. As the SDK calls join() already on shutdown, refer to flush() to ensure all events arive at the Langfuse API.
2088class StateType(Enum): 2089 """Enum to distinguish observation and trace states. 2090 2091 Attributes: 2092 OBSERVATION (int): Observation state. 2093 TRACE (int): Trace state. 2094 """ 2095 2096 OBSERVATION = 1 2097 TRACE = 0
Enum to distinguish observation and trace states.
Attributes:
- OBSERVATION (int): Observation state.
- TRACE (int): Trace state.
Inherited Members
- enum.Enum
- name
- value
2100class StatefulClient(object): 2101 """Base class for handling stateful operations in the Langfuse system. 2102 2103 This client is capable of creating different nested Langfuse objects like spans, generations, scores, and events, 2104 associating them with either an observation or a trace based on the specified state type. 2105 2106 Attributes: 2107 client (FernLangfuse): Core interface for Langfuse API interactions. 2108 id (str): Unique identifier of the stateful client (either observation or trace). 2109 state_type (StateType): Enum indicating whether the client is an observation or a trace. 2110 trace_id (str): Id of the trace associated with the stateful client. 2111 task_manager (TaskManager): Manager handling asynchronous tasks for the client. 2112 """ 2113 2114 log = logging.getLogger("langfuse") 2115 2116 def __init__( 2117 self, 2118 client: FernLangfuse, 2119 id: str, 2120 state_type: StateType, 2121 trace_id: str, 2122 task_manager: TaskManager, 2123 ): 2124 """Initialize the StatefulClient. 2125 2126 Args: 2127 client (FernLangfuse): Core interface for Langfuse API interactions. 2128 id (str): Unique identifier of the stateful client (either observation or trace). 2129 state_type (StateType): Enum indicating whether the client is an observation or a trace. 2130 trace_id (str): Id of the trace associated with the stateful client. 2131 task_manager (TaskManager): Manager handling asynchronous tasks for the client. 2132 """ 2133 self.client = client 2134 self.trace_id = trace_id 2135 self.id = id 2136 self.state_type = state_type 2137 self.task_manager = task_manager 2138 2139 def _add_state_to_event(self, body: dict): 2140 if self.state_type == StateType.OBSERVATION: 2141 body["parent_observation_id"] = self.id 2142 body["trace_id"] = self.trace_id 2143 else: 2144 body["trace_id"] = self.id 2145 return body 2146 2147 def _add_default_values(self, body: dict): 2148 if body.get("start_time") is None: 2149 body["start_time"] = _get_timestamp() 2150 return body 2151 2152 def generation( 2153 self, 2154 *, 2155 id: typing.Optional[str] = None, 2156 name: typing.Optional[str] = None, 2157 start_time: typing.Optional[dt.datetime] = None, 2158 end_time: typing.Optional[dt.datetime] = None, 2159 metadata: typing.Optional[typing.Any] = None, 2160 level: typing.Optional[SpanLevel] = None, 2161 status_message: typing.Optional[str] = None, 2162 version: typing.Optional[str] = None, 2163 completion_start_time: typing.Optional[dt.datetime] = None, 2164 model: typing.Optional[str] = None, 2165 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 2166 input: typing.Optional[typing.Any] = None, 2167 output: typing.Optional[typing.Any] = None, 2168 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 2169 usage_details: typing.Optional[typing.Dict[str, int]] = None, 2170 cost_details: typing.Optional[typing.Dict[str, float]] = None, 2171 prompt: typing.Optional[PromptClient] = None, 2172 **kwargs, 2173 ) -> "StatefulGenerationClient": 2174 """Create a generation nested within the current observation or trace. 2175 2176 A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI. 2177 2178 Args: 2179 id (Optional[str]): The id of the generation can be set, defaults to random id. 2180 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 2181 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 2182 end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`. 2183 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 2184 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 2185 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2186 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 2187 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2188 model (Optional[str]): The name of the model used for the generation. 2189 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 2190 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 2191 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 2192 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 2193 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 2194 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 2195 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 2196 **kwargs: Additional keyword arguments to include in the generation. 2197 2198 Returns: 2199 StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations. 2200 2201 Example: 2202 ```python 2203 from langfuse import Langfuse 2204 2205 langfuse = Langfuse() 2206 2207 # Create a trace 2208 trace = langfuse.trace(name = "llm-feature") 2209 2210 # Create a nested generation in Langfuse 2211 generation = trace.generation( 2212 name="summary-generation", 2213 model="gpt-3.5-turbo", 2214 model_parameters={"maxTokens": "1000", "temperature": "0.9"}, 2215 input=[{"role": "system", "content": "You are a helpful assistant."}, 2216 {"role": "user", "content": "Please generate a summary of the following documents ..."}], 2217 metadata={"interface": "whatsapp"} 2218 ) 2219 ``` 2220 """ 2221 generation_id = id or str(uuid.uuid4()) 2222 try: 2223 generation_body = { 2224 "id": generation_id, 2225 "name": name, 2226 "start_time": start_time or _get_timestamp(), 2227 "metadata": metadata, 2228 "level": level, 2229 "status_message": status_message, 2230 "version": version, 2231 "end_time": end_time, 2232 "completion_start_time": completion_start_time, 2233 "model": model, 2234 "model_parameters": model_parameters, 2235 "input": input, 2236 "output": output, 2237 "usage": _convert_usage_input(usage) if usage is not None else None, 2238 "usage_details": usage_details, 2239 "cost_details": cost_details, 2240 **_create_prompt_context(prompt), 2241 **kwargs, 2242 } 2243 2244 generation_body = self._add_state_to_event(generation_body) 2245 new_body = self._add_default_values(generation_body) 2246 2247 new_body = CreateGenerationBody(**new_body) 2248 2249 event = { 2250 "id": str(uuid.uuid4()), 2251 "type": "generation-create", 2252 "body": new_body.dict(exclude_none=True, exclude_unset=False), 2253 } 2254 2255 self.log.debug( 2256 f"Creating generation {_filter_io_from_event_body(generation_body)}..." 2257 ) 2258 self.task_manager.add_task(event) 2259 2260 except Exception as e: 2261 self.log.exception(e) 2262 finally: 2263 return StatefulGenerationClient( 2264 self.client, 2265 generation_id, 2266 StateType.OBSERVATION, 2267 self.trace_id, 2268 task_manager=self.task_manager, 2269 ) 2270 2271 def span( 2272 self, 2273 *, 2274 id: typing.Optional[str] = None, 2275 name: typing.Optional[str] = None, 2276 start_time: typing.Optional[dt.datetime] = None, 2277 end_time: typing.Optional[dt.datetime] = None, 2278 metadata: typing.Optional[typing.Any] = None, 2279 input: typing.Optional[typing.Any] = None, 2280 output: typing.Optional[typing.Any] = None, 2281 level: typing.Optional[SpanLevel] = None, 2282 status_message: typing.Optional[str] = None, 2283 version: typing.Optional[str] = None, 2284 **kwargs, 2285 ) -> "StatefulSpanClient": 2286 """Create a span nested within the current observation or trace. 2287 2288 A span represents durations of units of work in a trace. 2289 2290 Args: 2291 id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id. 2292 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 2293 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 2294 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 2295 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 2296 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2297 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 2298 input (Optional[dict]): The input to the span. Can be any JSON object. 2299 output (Optional[dict]): The output to the span. Can be any JSON object. 2300 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2301 **kwargs: Additional keyword arguments to include in the span. 2302 2303 Returns: 2304 StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations. 2305 2306 Example: 2307 ```python 2308 from langfuse import Langfuse 2309 2310 langfuse = Langfuse() 2311 2312 # Create a trace 2313 trace = langfuse.trace(name = "llm-feature") 2314 2315 # Create a span 2316 retrieval = langfuse.span(name = "retrieval") 2317 ``` 2318 """ 2319 span_id = id or str(uuid.uuid4()) 2320 try: 2321 span_body = { 2322 "id": span_id, 2323 "name": name, 2324 "start_time": start_time or _get_timestamp(), 2325 "metadata": metadata, 2326 "input": input, 2327 "output": output, 2328 "level": level, 2329 "status_message": status_message, 2330 "version": version, 2331 "end_time": end_time, 2332 **kwargs, 2333 } 2334 2335 self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...") 2336 2337 new_dict = self._add_state_to_event(span_body) 2338 new_body = self._add_default_values(new_dict) 2339 2340 event = CreateSpanBody(**new_body) 2341 2342 event = { 2343 "id": str(uuid.uuid4()), 2344 "type": "span-create", 2345 "body": event, 2346 } 2347 2348 self.task_manager.add_task(event) 2349 except Exception as e: 2350 self.log.exception(e) 2351 finally: 2352 return StatefulSpanClient( 2353 self.client, 2354 span_id, 2355 StateType.OBSERVATION, 2356 self.trace_id, 2357 task_manager=self.task_manager, 2358 ) 2359 2360 @overload 2361 def score( 2362 self, 2363 *, 2364 id: typing.Optional[str] = None, 2365 name: str, 2366 value: float, 2367 data_type: typing.Optional[Literal["NUMERIC", "BOOLEAN"]] = None, 2368 comment: typing.Optional[str] = None, 2369 config_id: typing.Optional[str] = None, 2370 **kwargs, 2371 ) -> "StatefulClient": ... 2372 2373 @overload 2374 def score( 2375 self, 2376 *, 2377 id: typing.Optional[str] = None, 2378 name: str, 2379 value: str, 2380 data_type: typing.Optional[Literal["CATEGORICAL"]] = "CATEGORICAL", 2381 comment: typing.Optional[str] = None, 2382 config_id: typing.Optional[str] = None, 2383 **kwargs, 2384 ) -> "StatefulClient": ... 2385 2386 def score( 2387 self, 2388 *, 2389 id: typing.Optional[str] = None, 2390 name: str, 2391 value: typing.Union[float, str], 2392 data_type: typing.Optional[ScoreDataType] = None, 2393 comment: typing.Optional[str] = None, 2394 config_id: typing.Optional[str] = None, 2395 **kwargs, 2396 ) -> "StatefulClient": 2397 """Create a score attached for the current observation or trace. 2398 2399 Args: 2400 name (str): Identifier of the score. 2401 value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores. 2402 data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. 2403 When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores. 2404 comment (Optional[str]): Additional context/explanation of the score. 2405 id (Optional[str]): The id of the score. If not provided, a new UUID is generated. 2406 config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None. 2407 **kwargs: Additional keyword arguments to include in the score. 2408 2409 Returns: 2410 StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining. 2411 2412 Example: 2413 ```python 2414 from langfuse import Langfuse 2415 2416 langfuse = Langfuse() 2417 2418 # Create a trace 2419 trace = langfuse.trace(name="example-application") 2420 2421 # Add score to the trace 2422 trace = trace.score( 2423 name="user-explicit-feedback", 2424 value=0.8, 2425 comment="I like how personalized the response is" 2426 ) 2427 ``` 2428 """ 2429 score_id = id or str(uuid.uuid4()) 2430 try: 2431 new_score = { 2432 "id": score_id, 2433 "trace_id": self.trace_id, 2434 "name": name, 2435 "value": value, 2436 "data_type": data_type, 2437 "comment": comment, 2438 "config_id": config_id, 2439 **kwargs, 2440 } 2441 2442 self.log.debug(f"Creating score {new_score}...") 2443 2444 new_dict = self._add_state_to_event(new_score) 2445 2446 if self.state_type == StateType.OBSERVATION: 2447 new_dict["observationId"] = self.id 2448 2449 request = ScoreBody(**new_dict) 2450 2451 event = { 2452 "id": str(uuid.uuid4()), 2453 "type": "score-create", 2454 "body": request, 2455 } 2456 2457 self.task_manager.add_task(event) 2458 2459 except Exception as e: 2460 self.log.exception(e) 2461 finally: 2462 return StatefulClient( 2463 self.client, 2464 self.id, 2465 self.state_type, 2466 self.trace_id, 2467 task_manager=self.task_manager, 2468 ) 2469 2470 def event( 2471 self, 2472 *, 2473 id: typing.Optional[str] = None, 2474 name: typing.Optional[str] = None, 2475 start_time: typing.Optional[dt.datetime] = None, 2476 metadata: typing.Optional[typing.Any] = None, 2477 input: typing.Optional[typing.Any] = None, 2478 output: typing.Optional[typing.Any] = None, 2479 level: typing.Optional[SpanLevel] = None, 2480 status_message: typing.Optional[str] = None, 2481 version: typing.Optional[str] = None, 2482 **kwargs, 2483 ) -> "StatefulClient": 2484 """Create an event nested within the current observation or trace. 2485 2486 An event represents a discrete event in a trace. 2487 2488 Args: 2489 id (Optional[str]): The id of the event can be set, otherwise a random id is generated. 2490 name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI. 2491 start_time (Optional[datetime]): The time at which the event started, defaults to the current time. 2492 metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API. 2493 input (Optional[Any]): The input to the event. Can be any JSON object. 2494 output (Optional[Any]): The output to the event. Can be any JSON object. 2495 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2496 status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event. 2497 version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging. 2498 **kwargs: Additional keyword arguments to include in the event. 2499 2500 Returns: 2501 StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations. 2502 2503 Example: 2504 ```python 2505 from langfuse import Langfuse 2506 2507 langfuse = Langfuse() 2508 2509 # Create a trace 2510 trace = langfuse.trace(name = "llm-feature") 2511 2512 # Create an event 2513 retrieval = trace.event(name = "retrieval") 2514 ``` 2515 """ 2516 event_id = id or str(uuid.uuid4()) 2517 try: 2518 event_body = { 2519 "id": event_id, 2520 "name": name, 2521 "start_time": start_time or _get_timestamp(), 2522 "metadata": metadata, 2523 "input": input, 2524 "output": output, 2525 "level": level, 2526 "status_message": status_message, 2527 "version": version, 2528 **kwargs, 2529 } 2530 2531 new_dict = self._add_state_to_event(event_body) 2532 new_body = self._add_default_values(new_dict) 2533 2534 request = CreateEventBody(**new_body) 2535 2536 event = { 2537 "id": str(uuid.uuid4()), 2538 "type": "event-create", 2539 "body": request, 2540 } 2541 2542 self.log.debug( 2543 f"Creating event {_filter_io_from_event_body(event_body)}..." 2544 ) 2545 self.task_manager.add_task(event) 2546 2547 except Exception as e: 2548 self.log.exception(e) 2549 finally: 2550 return StatefulClient( 2551 self.client, 2552 event_id, 2553 StateType.OBSERVATION, 2554 self.trace_id, 2555 self.task_manager, 2556 ) 2557 2558 def get_trace_url(self): 2559 """Get the URL to see the current trace in the Langfuse UI.""" 2560 return f"{self.client._client_wrapper._base_url}/trace/{self.trace_id}"
Base class for handling stateful operations in the Langfuse system.
This client is capable of creating different nested Langfuse objects like spans, generations, scores, and events, associating them with either an observation or a trace based on the specified state type.
Attributes:
- client (FernLangfuse): Core interface for Langfuse API interactions.
- id (str): Unique identifier of the stateful client (either observation or trace).
- state_type (StateType): Enum indicating whether the client is an observation or a trace.
- trace_id (str): Id of the trace associated with the stateful client.
- task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2116 def __init__( 2117 self, 2118 client: FernLangfuse, 2119 id: str, 2120 state_type: StateType, 2121 trace_id: str, 2122 task_manager: TaskManager, 2123 ): 2124 """Initialize the StatefulClient. 2125 2126 Args: 2127 client (FernLangfuse): Core interface for Langfuse API interactions. 2128 id (str): Unique identifier of the stateful client (either observation or trace). 2129 state_type (StateType): Enum indicating whether the client is an observation or a trace. 2130 trace_id (str): Id of the trace associated with the stateful client. 2131 task_manager (TaskManager): Manager handling asynchronous tasks for the client. 2132 """ 2133 self.client = client 2134 self.trace_id = trace_id 2135 self.id = id 2136 self.state_type = state_type 2137 self.task_manager = task_manager
Initialize the StatefulClient.
Arguments:
- client (FernLangfuse): Core interface for Langfuse API interactions.
- id (str): Unique identifier of the stateful client (either observation or trace).
- state_type (StateType): Enum indicating whether the client is an observation or a trace.
- trace_id (str): Id of the trace associated with the stateful client.
- task_manager (TaskManager): Manager handling asynchronous tasks for the client.
2152 def generation( 2153 self, 2154 *, 2155 id: typing.Optional[str] = None, 2156 name: typing.Optional[str] = None, 2157 start_time: typing.Optional[dt.datetime] = None, 2158 end_time: typing.Optional[dt.datetime] = None, 2159 metadata: typing.Optional[typing.Any] = None, 2160 level: typing.Optional[SpanLevel] = None, 2161 status_message: typing.Optional[str] = None, 2162 version: typing.Optional[str] = None, 2163 completion_start_time: typing.Optional[dt.datetime] = None, 2164 model: typing.Optional[str] = None, 2165 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 2166 input: typing.Optional[typing.Any] = None, 2167 output: typing.Optional[typing.Any] = None, 2168 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 2169 usage_details: typing.Optional[typing.Dict[str, int]] = None, 2170 cost_details: typing.Optional[typing.Dict[str, float]] = None, 2171 prompt: typing.Optional[PromptClient] = None, 2172 **kwargs, 2173 ) -> "StatefulGenerationClient": 2174 """Create a generation nested within the current observation or trace. 2175 2176 A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI. 2177 2178 Args: 2179 id (Optional[str]): The id of the generation can be set, defaults to random id. 2180 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 2181 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 2182 end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`. 2183 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 2184 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 2185 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2186 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 2187 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2188 model (Optional[str]): The name of the model used for the generation. 2189 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 2190 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 2191 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 2192 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 2193 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 2194 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 2195 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 2196 **kwargs: Additional keyword arguments to include in the generation. 2197 2198 Returns: 2199 StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations. 2200 2201 Example: 2202 ```python 2203 from langfuse import Langfuse 2204 2205 langfuse = Langfuse() 2206 2207 # Create a trace 2208 trace = langfuse.trace(name = "llm-feature") 2209 2210 # Create a nested generation in Langfuse 2211 generation = trace.generation( 2212 name="summary-generation", 2213 model="gpt-3.5-turbo", 2214 model_parameters={"maxTokens": "1000", "temperature": "0.9"}, 2215 input=[{"role": "system", "content": "You are a helpful assistant."}, 2216 {"role": "user", "content": "Please generate a summary of the following documents ..."}], 2217 metadata={"interface": "whatsapp"} 2218 ) 2219 ``` 2220 """ 2221 generation_id = id or str(uuid.uuid4()) 2222 try: 2223 generation_body = { 2224 "id": generation_id, 2225 "name": name, 2226 "start_time": start_time or _get_timestamp(), 2227 "metadata": metadata, 2228 "level": level, 2229 "status_message": status_message, 2230 "version": version, 2231 "end_time": end_time, 2232 "completion_start_time": completion_start_time, 2233 "model": model, 2234 "model_parameters": model_parameters, 2235 "input": input, 2236 "output": output, 2237 "usage": _convert_usage_input(usage) if usage is not None else None, 2238 "usage_details": usage_details, 2239 "cost_details": cost_details, 2240 **_create_prompt_context(prompt), 2241 **kwargs, 2242 } 2243 2244 generation_body = self._add_state_to_event(generation_body) 2245 new_body = self._add_default_values(generation_body) 2246 2247 new_body = CreateGenerationBody(**new_body) 2248 2249 event = { 2250 "id": str(uuid.uuid4()), 2251 "type": "generation-create", 2252 "body": new_body.dict(exclude_none=True, exclude_unset=False), 2253 } 2254 2255 self.log.debug( 2256 f"Creating generation {_filter_io_from_event_body(generation_body)}..." 2257 ) 2258 self.task_manager.add_task(event) 2259 2260 except Exception as e: 2261 self.log.exception(e) 2262 finally: 2263 return StatefulGenerationClient( 2264 self.client, 2265 generation_id, 2266 StateType.OBSERVATION, 2267 self.trace_id, 2268 task_manager=self.task_manager, 2269 )
Create a generation nested within the current observation or trace.
A generation is a span that is used to log generations of AI models. They contain additional metadata about the model, the prompt/completion, the cost of executing the model and are specifically rendered in the langfuse UI.
Arguments:
- id (Optional[str]): The id of the generation can be set, defaults to random id.
- name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
- end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by
generation.end()
. - completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
- metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
- level (Optional[str]): The level of the generation. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. - status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
- version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
- model (Optional[str]): The name of the model used for the generation.
- model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
- input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
- output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
- usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {
promptTokens
,completionTokens
,totalTokens
} and a more generic version {input
,output
,total
,unit
,inputCost
,outputCost
,totalCost
} where unit can be of value"TOKENS"
,"CHARACTERS"
,"MILLISECONDS"
,"SECONDS"
, or"IMAGES"
. Refer to the docs on how to automatically infer token usage and costs in Langfuse. - usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
- cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
- prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
- **kwargs: Additional keyword arguments to include in the generation.
Returns:
StatefulGenerationClient: The created generation. Use this client to update the generation or create additional nested observations.
Example:
from langfuse import Langfuse langfuse = Langfuse() # Create a trace trace = langfuse.trace(name = "llm-feature") # Create a nested generation in Langfuse generation = trace.generation( name="summary-generation", model="gpt-3.5-turbo", model_parameters={"maxTokens": "1000", "temperature": "0.9"}, input=[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Please generate a summary of the following documents ..."}], metadata={"interface": "whatsapp"} )
2271 def span( 2272 self, 2273 *, 2274 id: typing.Optional[str] = None, 2275 name: typing.Optional[str] = None, 2276 start_time: typing.Optional[dt.datetime] = None, 2277 end_time: typing.Optional[dt.datetime] = None, 2278 metadata: typing.Optional[typing.Any] = None, 2279 input: typing.Optional[typing.Any] = None, 2280 output: typing.Optional[typing.Any] = None, 2281 level: typing.Optional[SpanLevel] = None, 2282 status_message: typing.Optional[str] = None, 2283 version: typing.Optional[str] = None, 2284 **kwargs, 2285 ) -> "StatefulSpanClient": 2286 """Create a span nested within the current observation or trace. 2287 2288 A span represents durations of units of work in a trace. 2289 2290 Args: 2291 id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id. 2292 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 2293 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 2294 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 2295 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 2296 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2297 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 2298 input (Optional[dict]): The input to the span. Can be any JSON object. 2299 output (Optional[dict]): The output to the span. Can be any JSON object. 2300 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2301 **kwargs: Additional keyword arguments to include in the span. 2302 2303 Returns: 2304 StatefulSpanClient: The created span. Use this client to update the span or create additional nested observations. 2305 2306 Example: 2307 ```python 2308 from langfuse import Langfuse 2309 2310 langfuse = Langfuse() 2311 2312 # Create a trace 2313 trace = langfuse.trace(name = "llm-feature") 2314 2315 # Create a span 2316 retrieval = langfuse.span(name = "retrieval") 2317 ``` 2318 """ 2319 span_id = id or str(uuid.uuid4()) 2320 try: 2321 span_body = { 2322 "id": span_id, 2323 "name": name, 2324 "start_time": start_time or _get_timestamp(), 2325 "metadata": metadata, 2326 "input": input, 2327 "output": output, 2328 "level": level, 2329 "status_message": status_message, 2330 "version": version, 2331 "end_time": end_time, 2332 **kwargs, 2333 } 2334 2335 self.log.debug(f"Creating span {_filter_io_from_event_body(span_body)}...") 2336 2337 new_dict = self._add_state_to_event(span_body) 2338 new_body = self._add_default_values(new_dict) 2339 2340 event = CreateSpanBody(**new_body) 2341 2342 event = { 2343 "id": str(uuid.uuid4()), 2344 "type": "span-create", 2345 "body": event, 2346 } 2347 2348 self.task_manager.add_task(event) 2349 except Exception as e: 2350 self.log.exception(e) 2351 finally: 2352 return StatefulSpanClient( 2353 self.client, 2354 span_id, 2355 StateType.OBSERVATION, 2356 self.trace_id, 2357 task_manager=self.task_manager, 2358 )
Create a span nested within the current observation or trace.
A span represents durations of units of work in a trace.
Arguments:
- id (Optional[str]): The id of the span can be set, otherwise a random id is generated. Spans are upserted on id.
- name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
- end_time (Optional[datetime]): The time at which the span ended. Automatically set by
span.end()
. - metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
- level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. 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")
2386 def score( 2387 self, 2388 *, 2389 id: typing.Optional[str] = None, 2390 name: str, 2391 value: typing.Union[float, str], 2392 data_type: typing.Optional[ScoreDataType] = None, 2393 comment: typing.Optional[str] = None, 2394 config_id: typing.Optional[str] = None, 2395 **kwargs, 2396 ) -> "StatefulClient": 2397 """Create a score attached for the current observation or trace. 2398 2399 Args: 2400 name (str): Identifier of the score. 2401 value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores. 2402 data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. 2403 When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores. 2404 comment (Optional[str]): Additional context/explanation of the score. 2405 id (Optional[str]): The id of the score. If not provided, a new UUID is generated. 2406 config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None. 2407 **kwargs: Additional keyword arguments to include in the score. 2408 2409 Returns: 2410 StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining. 2411 2412 Example: 2413 ```python 2414 from langfuse import Langfuse 2415 2416 langfuse = Langfuse() 2417 2418 # Create a trace 2419 trace = langfuse.trace(name="example-application") 2420 2421 # Add score to the trace 2422 trace = trace.score( 2423 name="user-explicit-feedback", 2424 value=0.8, 2425 comment="I like how personalized the response is" 2426 ) 2427 ``` 2428 """ 2429 score_id = id or str(uuid.uuid4()) 2430 try: 2431 new_score = { 2432 "id": score_id, 2433 "trace_id": self.trace_id, 2434 "name": name, 2435 "value": value, 2436 "data_type": data_type, 2437 "comment": comment, 2438 "config_id": config_id, 2439 **kwargs, 2440 } 2441 2442 self.log.debug(f"Creating score {new_score}...") 2443 2444 new_dict = self._add_state_to_event(new_score) 2445 2446 if self.state_type == StateType.OBSERVATION: 2447 new_dict["observationId"] = self.id 2448 2449 request = ScoreBody(**new_dict) 2450 2451 event = { 2452 "id": str(uuid.uuid4()), 2453 "type": "score-create", 2454 "body": request, 2455 } 2456 2457 self.task_manager.add_task(event) 2458 2459 except Exception as e: 2460 self.log.exception(e) 2461 finally: 2462 return StatefulClient( 2463 self.client, 2464 self.id, 2465 self.state_type, 2466 self.trace_id, 2467 task_manager=self.task_manager, 2468 )
Create a score attached for the current observation or trace.
Arguments:
- name (str): Identifier of the score.
- value (Union[float, str]): The value of the score. Should be passed as float for numeric and boolean scores and as string for categorical scores.
- data_type (Optional[ScoreDataType]): The data type of the score. When not set, the data type is inferred from the score config's data type, when present. When no config is set, the data type is inferred from the value's type, i.e. float values are categorized as numeric scores and string values as categorical scores.
- comment (Optional[str]): Additional context/explanation of the score.
- id (Optional[str]): The id of the score. If not provided, a new UUID is generated.
- config_id (Optional[str]): The id of the score config. When set, the score value is validated against the config. Defaults to None.
- **kwargs: Additional keyword arguments to include in the score.
Returns:
StatefulClient: The current observation or trace for which the score was created. Passthrough for chaining.
Example:
from langfuse import Langfuse langfuse = Langfuse() # Create a trace trace = langfuse.trace(name="example-application") # Add score to the trace trace = trace.score( name="user-explicit-feedback", value=0.8, comment="I like how personalized the response is" )
2470 def event( 2471 self, 2472 *, 2473 id: typing.Optional[str] = None, 2474 name: typing.Optional[str] = None, 2475 start_time: typing.Optional[dt.datetime] = None, 2476 metadata: typing.Optional[typing.Any] = None, 2477 input: typing.Optional[typing.Any] = None, 2478 output: typing.Optional[typing.Any] = None, 2479 level: typing.Optional[SpanLevel] = None, 2480 status_message: typing.Optional[str] = None, 2481 version: typing.Optional[str] = None, 2482 **kwargs, 2483 ) -> "StatefulClient": 2484 """Create an event nested within the current observation or trace. 2485 2486 An event represents a discrete event in a trace. 2487 2488 Args: 2489 id (Optional[str]): The id of the event can be set, otherwise a random id is generated. 2490 name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI. 2491 start_time (Optional[datetime]): The time at which the event started, defaults to the current time. 2492 metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API. 2493 input (Optional[Any]): The input to the event. Can be any JSON object. 2494 output (Optional[Any]): The output to the event. Can be any JSON object. 2495 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2496 status_message (Optional[str]): The status message of the event. Additional field for context of the event. E.g. the error message of an error event. 2497 version (Optional[str]): The version of the event type. Used to understand how changes to the event type affect metrics. Useful in debugging. 2498 **kwargs: Additional keyword arguments to include in the event. 2499 2500 Returns: 2501 StatefulSpanClient: The created event. Use this client to update the event or create additional nested observations. 2502 2503 Example: 2504 ```python 2505 from langfuse import Langfuse 2506 2507 langfuse = Langfuse() 2508 2509 # Create a trace 2510 trace = langfuse.trace(name = "llm-feature") 2511 2512 # Create an event 2513 retrieval = trace.event(name = "retrieval") 2514 ``` 2515 """ 2516 event_id = id or str(uuid.uuid4()) 2517 try: 2518 event_body = { 2519 "id": event_id, 2520 "name": name, 2521 "start_time": start_time or _get_timestamp(), 2522 "metadata": metadata, 2523 "input": input, 2524 "output": output, 2525 "level": level, 2526 "status_message": status_message, 2527 "version": version, 2528 **kwargs, 2529 } 2530 2531 new_dict = self._add_state_to_event(event_body) 2532 new_body = self._add_default_values(new_dict) 2533 2534 request = CreateEventBody(**new_body) 2535 2536 event = { 2537 "id": str(uuid.uuid4()), 2538 "type": "event-create", 2539 "body": request, 2540 } 2541 2542 self.log.debug( 2543 f"Creating event {_filter_io_from_event_body(event_body)}..." 2544 ) 2545 self.task_manager.add_task(event) 2546 2547 except Exception as e: 2548 self.log.exception(e) 2549 finally: 2550 return StatefulClient( 2551 self.client, 2552 event_id, 2553 StateType.OBSERVATION, 2554 self.trace_id, 2555 self.task_manager, 2556 )
Create an event nested within the current observation or trace.
An event represents a discrete event in a trace.
Arguments:
- id (Optional[str]): The id of the event can be set, otherwise a random id is generated.
- name (Optional[str]): Identifier of the event. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime]): The time at which the event started, defaults to the current time.
- metadata (Optional[Any]): Additional metadata of the event. Can be any JSON object. Metadata is merged when being updated via the API.
- input (Optional[Any]): The input to the event. Can be any JSON object.
- output (Optional[Any]): The output to the event. Can be any JSON object.
- level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the event. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. 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")
2563class StatefulGenerationClient(StatefulClient): 2564 """Class for handling stateful operations of generations in the Langfuse system. Inherits from StatefulClient. 2565 2566 This client extends the capabilities of the StatefulClient to specifically handle generation, 2567 allowing for the creation, update, and termination of generation processes in Langfuse. 2568 2569 Attributes: 2570 client (FernLangfuse): Core interface for Langfuse API interaction. 2571 id (str): Unique identifier of the generation. 2572 state_type (StateType): Type of the stateful entity (observation or trace). 2573 trace_id (str): Id of trace associated with the generation. 2574 task_manager (TaskManager): Manager for handling asynchronous tasks. 2575 """ 2576 2577 log = logging.getLogger("langfuse") 2578 2579 def __init__( 2580 self, 2581 client: FernLangfuse, 2582 id: str, 2583 state_type: StateType, 2584 trace_id: str, 2585 task_manager: TaskManager, 2586 ): 2587 """Initialize the StatefulGenerationClient.""" 2588 super().__init__(client, id, state_type, trace_id, task_manager) 2589 2590 # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY 2591 def update( 2592 self, 2593 *, 2594 name: typing.Optional[str] = None, 2595 start_time: typing.Optional[dt.datetime] = None, 2596 end_time: typing.Optional[dt.datetime] = None, 2597 completion_start_time: typing.Optional[dt.datetime] = None, 2598 metadata: typing.Optional[typing.Any] = None, 2599 level: typing.Optional[SpanLevel] = None, 2600 status_message: typing.Optional[str] = None, 2601 version: typing.Optional[str] = None, 2602 model: typing.Optional[str] = None, 2603 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 2604 input: typing.Optional[typing.Any] = None, 2605 output: typing.Optional[typing.Any] = None, 2606 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 2607 usage_details: typing.Optional[typing.Dict[str, int]] = None, 2608 cost_details: typing.Optional[typing.Dict[str, float]] = None, 2609 prompt: typing.Optional[PromptClient] = None, 2610 **kwargs, 2611 ) -> "StatefulGenerationClient": 2612 """Update the generation. 2613 2614 Args: 2615 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 2616 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 2617 end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`. 2618 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 2619 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 2620 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2621 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 2622 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2623 model (Optional[str]): The name of the model used for the generation. 2624 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 2625 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 2626 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 2627 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 2628 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 2629 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 2630 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 2631 **kwargs: Additional keyword arguments to include in the generation. 2632 2633 Returns: 2634 StatefulGenerationClient: The updated generation. Passthrough for chaining. 2635 2636 Example: 2637 ```python 2638 from langfuse import Langfuse 2639 2640 langfuse = Langfuse() 2641 2642 # Create a trace 2643 trace = langfuse.trace(name = "llm-feature") 2644 2645 # Create a nested generation in Langfuse 2646 generation = trace.generation(name="summary-generation") 2647 2648 # Update the generation 2649 generation = generation.update(metadata={"interface": "whatsapp"}) 2650 ``` 2651 """ 2652 try: 2653 generation_body = { 2654 "id": self.id, 2655 "trace_id": self.trace_id, # Included to avoid relying on the order of events sent to the API 2656 "name": name, 2657 "start_time": start_time, 2658 "metadata": metadata, 2659 "level": level, 2660 "status_message": status_message, 2661 "version": version, 2662 "end_time": end_time, 2663 "completion_start_time": completion_start_time, 2664 "model": model, 2665 "model_parameters": model_parameters, 2666 "input": input, 2667 "output": output, 2668 "usage": _convert_usage_input(usage) if usage is not None else None, 2669 "usage_details": usage_details, 2670 "cost_details": cost_details, 2671 **_create_prompt_context(prompt), 2672 **kwargs, 2673 } 2674 2675 self.log.debug( 2676 f"Update generation {_filter_io_from_event_body(generation_body)}..." 2677 ) 2678 2679 request = UpdateGenerationBody(**generation_body) 2680 2681 event = { 2682 "id": str(uuid.uuid4()), 2683 "type": "generation-update", 2684 "body": request.dict(exclude_none=True, exclude_unset=False), 2685 } 2686 2687 self.log.debug( 2688 f"Update generation {_filter_io_from_event_body(generation_body)}..." 2689 ) 2690 self.task_manager.add_task(event) 2691 2692 except Exception as e: 2693 self.log.exception(e) 2694 finally: 2695 return StatefulGenerationClient( 2696 self.client, 2697 self.id, 2698 StateType.OBSERVATION, 2699 self.trace_id, 2700 task_manager=self.task_manager, 2701 ) 2702 2703 def end( 2704 self, 2705 *, 2706 name: typing.Optional[str] = None, 2707 start_time: typing.Optional[dt.datetime] = None, 2708 end_time: typing.Optional[dt.datetime] = None, 2709 completion_start_time: typing.Optional[dt.datetime] = None, 2710 metadata: typing.Optional[typing.Any] = None, 2711 level: typing.Optional[SpanLevel] = None, 2712 status_message: typing.Optional[str] = None, 2713 version: typing.Optional[str] = None, 2714 model: typing.Optional[str] = None, 2715 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 2716 input: typing.Optional[typing.Any] = None, 2717 output: typing.Optional[typing.Any] = None, 2718 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 2719 usage_details: typing.Optional[typing.Dict[str, int]] = None, 2720 cost_details: typing.Optional[typing.Dict[str, float]] = None, 2721 prompt: typing.Optional[PromptClient] = None, 2722 **kwargs, 2723 ) -> "StatefulGenerationClient": 2724 """End the generation, optionally updating its properties. 2725 2726 Args: 2727 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 2728 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 2729 end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time. 2730 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 2731 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 2732 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2733 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 2734 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2735 model (Optional[str]): The name of the model used for the generation. 2736 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 2737 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 2738 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 2739 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 2740 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 2741 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 2742 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 2743 **kwargs: Additional keyword arguments to include in the generation. 2744 2745 Returns: 2746 StatefulGenerationClient: The ended generation. Passthrough for chaining. 2747 2748 Example: 2749 ```python 2750 from langfuse import Langfuse 2751 2752 langfuse = Langfuse() 2753 2754 # Create a trace 2755 trace = langfuse.trace(name = "llm-feature") 2756 2757 # Create a nested generation in Langfuse 2758 generation = trace.generation(name="summary-generation") 2759 2760 # End the generation and update its properties 2761 generation = generation.end(metadata={"interface": "whatsapp"}) 2762 ``` 2763 """ 2764 return self.update( 2765 name=name, 2766 start_time=start_time, 2767 end_time=end_time or _get_timestamp(), 2768 metadata=metadata, 2769 level=level, 2770 status_message=status_message, 2771 version=version, 2772 completion_start_time=completion_start_time, 2773 model=model, 2774 model_parameters=model_parameters, 2775 input=input, 2776 output=output, 2777 usage=usage, 2778 usage_details=usage_details, 2779 cost_details=cost_details, 2780 prompt=prompt, 2781 **kwargs, 2782 )
Class for handling stateful operations of generations in the Langfuse system. Inherits from StatefulClient.
This client extends the capabilities of the StatefulClient to specifically handle generation, allowing for the creation, update, and termination of generation processes in Langfuse.
Attributes:
- client (FernLangfuse): Core interface for Langfuse API interaction.
- id (str): Unique identifier of the generation.
- state_type (StateType): Type of the stateful entity (observation or trace).
- trace_id (str): Id of trace associated with the generation.
- task_manager (TaskManager): Manager for handling asynchronous tasks.
2579 def __init__( 2580 self, 2581 client: FernLangfuse, 2582 id: str, 2583 state_type: StateType, 2584 trace_id: str, 2585 task_manager: TaskManager, 2586 ): 2587 """Initialize the StatefulGenerationClient.""" 2588 super().__init__(client, id, state_type, trace_id, task_manager)
Initialize the StatefulGenerationClient.
2591 def update( 2592 self, 2593 *, 2594 name: typing.Optional[str] = None, 2595 start_time: typing.Optional[dt.datetime] = None, 2596 end_time: typing.Optional[dt.datetime] = None, 2597 completion_start_time: typing.Optional[dt.datetime] = None, 2598 metadata: typing.Optional[typing.Any] = None, 2599 level: typing.Optional[SpanLevel] = None, 2600 status_message: typing.Optional[str] = None, 2601 version: typing.Optional[str] = None, 2602 model: typing.Optional[str] = None, 2603 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 2604 input: typing.Optional[typing.Any] = None, 2605 output: typing.Optional[typing.Any] = None, 2606 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 2607 usage_details: typing.Optional[typing.Dict[str, int]] = None, 2608 cost_details: typing.Optional[typing.Dict[str, float]] = None, 2609 prompt: typing.Optional[PromptClient] = None, 2610 **kwargs, 2611 ) -> "StatefulGenerationClient": 2612 """Update the generation. 2613 2614 Args: 2615 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 2616 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 2617 end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by `generation.end()`. 2618 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 2619 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 2620 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2621 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 2622 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2623 model (Optional[str]): The name of the model used for the generation. 2624 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 2625 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 2626 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 2627 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 2628 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 2629 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 2630 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 2631 **kwargs: Additional keyword arguments to include in the generation. 2632 2633 Returns: 2634 StatefulGenerationClient: The updated generation. Passthrough for chaining. 2635 2636 Example: 2637 ```python 2638 from langfuse import Langfuse 2639 2640 langfuse = Langfuse() 2641 2642 # Create a trace 2643 trace = langfuse.trace(name = "llm-feature") 2644 2645 # Create a nested generation in Langfuse 2646 generation = trace.generation(name="summary-generation") 2647 2648 # Update the generation 2649 generation = generation.update(metadata={"interface": "whatsapp"}) 2650 ``` 2651 """ 2652 try: 2653 generation_body = { 2654 "id": self.id, 2655 "trace_id": self.trace_id, # Included to avoid relying on the order of events sent to the API 2656 "name": name, 2657 "start_time": start_time, 2658 "metadata": metadata, 2659 "level": level, 2660 "status_message": status_message, 2661 "version": version, 2662 "end_time": end_time, 2663 "completion_start_time": completion_start_time, 2664 "model": model, 2665 "model_parameters": model_parameters, 2666 "input": input, 2667 "output": output, 2668 "usage": _convert_usage_input(usage) if usage is not None else None, 2669 "usage_details": usage_details, 2670 "cost_details": cost_details, 2671 **_create_prompt_context(prompt), 2672 **kwargs, 2673 } 2674 2675 self.log.debug( 2676 f"Update generation {_filter_io_from_event_body(generation_body)}..." 2677 ) 2678 2679 request = UpdateGenerationBody(**generation_body) 2680 2681 event = { 2682 "id": str(uuid.uuid4()), 2683 "type": "generation-update", 2684 "body": request.dict(exclude_none=True, exclude_unset=False), 2685 } 2686 2687 self.log.debug( 2688 f"Update generation {_filter_io_from_event_body(generation_body)}..." 2689 ) 2690 self.task_manager.add_task(event) 2691 2692 except Exception as e: 2693 self.log.exception(e) 2694 finally: 2695 return StatefulGenerationClient( 2696 self.client, 2697 self.id, 2698 StateType.OBSERVATION, 2699 self.trace_id, 2700 task_manager=self.task_manager, 2701 )
Update the generation.
Arguments:
- name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
- end_time (Optional[datetime.datetime]): The time at which the generation ended. Automatically set by
generation.end()
. - completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
- metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
- level (Optional[str]): The level of the generation. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. - status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
- version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
- model (Optional[str]): The name of the model used for the generation.
- model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
- input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
- output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
- usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {
promptTokens
,completionTokens
,totalTokens
} and a more generic version {input
,output
,total
,unit
,inputCost
,outputCost
,totalCost
} where unit can be of value"TOKENS"
,"CHARACTERS"
,"MILLISECONDS"
,"SECONDS"
, or"IMAGES"
. Refer to the docs on how to automatically infer token usage and costs in Langfuse. - usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
- cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
- prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
- **kwargs: Additional keyword arguments to include in the generation.
Returns:
StatefulGenerationClient: The updated generation. Passthrough for chaining.
Example:
from langfuse import Langfuse langfuse = Langfuse() # Create a trace trace = langfuse.trace(name = "llm-feature") # Create a nested generation in Langfuse generation = trace.generation(name="summary-generation") # Update the generation generation = generation.update(metadata={"interface": "whatsapp"})
2703 def end( 2704 self, 2705 *, 2706 name: typing.Optional[str] = None, 2707 start_time: typing.Optional[dt.datetime] = None, 2708 end_time: typing.Optional[dt.datetime] = None, 2709 completion_start_time: typing.Optional[dt.datetime] = None, 2710 metadata: typing.Optional[typing.Any] = None, 2711 level: typing.Optional[SpanLevel] = None, 2712 status_message: typing.Optional[str] = None, 2713 version: typing.Optional[str] = None, 2714 model: typing.Optional[str] = None, 2715 model_parameters: typing.Optional[typing.Dict[str, MapValue]] = None, 2716 input: typing.Optional[typing.Any] = None, 2717 output: typing.Optional[typing.Any] = None, 2718 usage: typing.Optional[typing.Union[pydantic.BaseModel, ModelUsage]] = None, 2719 usage_details: typing.Optional[typing.Dict[str, int]] = None, 2720 cost_details: typing.Optional[typing.Dict[str, float]] = None, 2721 prompt: typing.Optional[PromptClient] = None, 2722 **kwargs, 2723 ) -> "StatefulGenerationClient": 2724 """End the generation, optionally updating its properties. 2725 2726 Args: 2727 name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI. 2728 start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time. 2729 end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time. 2730 completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration. 2731 metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API. 2732 level (Optional[str]): The level of the generation. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2733 status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event. 2734 version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2735 model (Optional[str]): The name of the model used for the generation. 2736 model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs. 2737 input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object. 2738 output (Optional[dict]): The completion generated by the model. Can be any string or JSON object. 2739 usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {`promptTokens`, `completionTokens`, `totalTokens`} and a more generic version {`input`, `output`, `total`, `unit`, `inputCost`, `outputCost`, `totalCost`} where unit can be of value `"TOKENS"`, `"CHARACTERS"`, `"MILLISECONDS"`, `"SECONDS"`, or `"IMAGES"`. Refer to the docs on how to [automatically infer](https://langfuse.com/docs/model-usage-and-cost) token usage and costs in Langfuse. 2740 usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}. 2741 cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}. 2742 prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation. 2743 **kwargs: Additional keyword arguments to include in the generation. 2744 2745 Returns: 2746 StatefulGenerationClient: The ended generation. Passthrough for chaining. 2747 2748 Example: 2749 ```python 2750 from langfuse import Langfuse 2751 2752 langfuse = Langfuse() 2753 2754 # Create a trace 2755 trace = langfuse.trace(name = "llm-feature") 2756 2757 # Create a nested generation in Langfuse 2758 generation = trace.generation(name="summary-generation") 2759 2760 # End the generation and update its properties 2761 generation = generation.end(metadata={"interface": "whatsapp"}) 2762 ``` 2763 """ 2764 return self.update( 2765 name=name, 2766 start_time=start_time, 2767 end_time=end_time or _get_timestamp(), 2768 metadata=metadata, 2769 level=level, 2770 status_message=status_message, 2771 version=version, 2772 completion_start_time=completion_start_time, 2773 model=model, 2774 model_parameters=model_parameters, 2775 input=input, 2776 output=output, 2777 usage=usage, 2778 usage_details=usage_details, 2779 cost_details=cost_details, 2780 prompt=prompt, 2781 **kwargs, 2782 )
End the generation, optionally updating its properties.
Arguments:
- name (Optional[str]): Identifier of the generation. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime.datetime]): The time at which the generation started, defaults to the current time.
- end_time (Optional[datetime.datetime]): Automatically set to the current time. Can be overridden to set a custom end time.
- completion_start_time (Optional[datetime.datetime]): The time at which the completion started (streaming). Set it to get latency analytics broken down into time until completion started and completion duration.
- metadata (Optional[dict]): Additional metadata of the generation. Can be any JSON object. Metadata is merged when being updated via the API.
- level (Optional[str]): The level of the generation. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. - status_message (Optional[str]): The status message of the generation. Additional field for context of the event. E.g. the error message of an error event.
- version (Optional[str]): The version of the generation type. Used to understand how changes to the span type affect metrics. Useful in debugging.
- model (Optional[str]): The name of the model used for the generation.
- model_parameters (Optional[dict]): The parameters of the model used for the generation; can be any key-value pairs.
- input (Optional[dict]): The prompt used for the generation. Can be any string or JSON object.
- output (Optional[dict]): The completion generated by the model. Can be any string or JSON object.
- usage (Optional[dict]): [DEPRECATED, use usage_details and cost_details instead] The usage object supports the OpenAi structure with {
promptTokens
,completionTokens
,totalTokens
} and a more generic version {input
,output
,total
,unit
,inputCost
,outputCost
,totalCost
} where unit can be of value"TOKENS"
,"CHARACTERS"
,"MILLISECONDS"
,"SECONDS"
, or"IMAGES"
. Refer to the docs on how to automatically infer token usage and costs in Langfuse. - usage_details (Optional[dict]): The usage details of the generation. Also accepts OpenAI usage details. Keys are the usage type (e.g. "input", "input_cached", "output") and values are integers representing the number of units used. For accurate cost calculations in Langfuse, ensure each usage type has a corresponding price configured in the Langfuse models table. Example: {"input": 500, "output": 150}.
- cost_details (Optional[dict]): The cost details of the generation. Keys are the usage type (e.g. "input", "input_cached", "output") and values are floats representing the cost in USD. Example: {"input": 0.0015, "output": 0.002}.
- prompt (Optional[PromptClient]): The Langfuse prompt object used for the generation.
- **kwargs: Additional keyword arguments to include in the generation.
Returns:
StatefulGenerationClient: The ended generation. Passthrough for chaining.
Example:
from langfuse import Langfuse langfuse = Langfuse() # Create a trace trace = langfuse.trace(name = "llm-feature") # Create a nested generation in Langfuse generation = trace.generation(name="summary-generation") # End the generation and update its properties generation = generation.end(metadata={"interface": "whatsapp"})
Inherited Members
2785class StatefulSpanClient(StatefulClient): 2786 """Class for handling stateful operations of spans in the Langfuse system. Inherits from StatefulClient. 2787 2788 Attributes: 2789 client (FernLangfuse): Core interface for Langfuse API interaction. 2790 id (str): Unique identifier of the span. 2791 state_type (StateType): Type of the stateful entity (observation or trace). 2792 trace_id (str): Id of trace associated with the span. 2793 task_manager (TaskManager): Manager for handling asynchronous tasks. 2794 """ 2795 2796 log = logging.getLogger("langfuse") 2797 2798 def __init__( 2799 self, 2800 client: FernLangfuse, 2801 id: str, 2802 state_type: StateType, 2803 trace_id: str, 2804 task_manager: TaskManager, 2805 ): 2806 """Initialize the StatefulSpanClient.""" 2807 super().__init__(client, id, state_type, trace_id, task_manager) 2808 2809 # WHEN CHANGING THIS METHOD, UPDATE END() FUNCTION ACCORDINGLY 2810 def update( 2811 self, 2812 *, 2813 name: typing.Optional[str] = None, 2814 start_time: typing.Optional[dt.datetime] = None, 2815 end_time: typing.Optional[dt.datetime] = None, 2816 metadata: typing.Optional[typing.Any] = None, 2817 input: typing.Optional[typing.Any] = None, 2818 output: typing.Optional[typing.Any] = None, 2819 level: typing.Optional[SpanLevel] = None, 2820 status_message: typing.Optional[str] = None, 2821 version: typing.Optional[str] = None, 2822 **kwargs, 2823 ) -> "StatefulSpanClient": 2824 """Update the span. 2825 2826 Args: 2827 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 2828 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 2829 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 2830 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 2831 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2832 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 2833 input (Optional[dict]): The input to the span. Can be any JSON object. 2834 output (Optional[dict]): The output to the span. Can be any JSON object. 2835 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2836 **kwargs: Additional keyword arguments to include in the span. 2837 2838 Returns: 2839 StatefulSpanClient: The updated span. Passthrough for chaining. 2840 2841 Example: 2842 ```python 2843 from langfuse import Langfuse 2844 2845 langfuse = Langfuse() 2846 2847 # Create a trace 2848 trace = langfuse.trace(name = "llm-feature") 2849 2850 # Create a nested span in Langfuse 2851 span = trace.span(name="retrieval") 2852 2853 # Update the span 2854 span = span.update(metadata={"interface": "whatsapp"}) 2855 ``` 2856 """ 2857 try: 2858 span_body = { 2859 "id": self.id, 2860 "trace_id": self.trace_id, # Included to avoid relying on the order of events sent to the API 2861 "name": name, 2862 "start_time": start_time, 2863 "metadata": metadata, 2864 "input": input, 2865 "output": output, 2866 "level": level, 2867 "status_message": status_message, 2868 "version": version, 2869 "end_time": end_time, 2870 **kwargs, 2871 } 2872 self.log.debug(f"Update span {_filter_io_from_event_body(span_body)}...") 2873 2874 request = UpdateSpanBody(**span_body) 2875 2876 event = { 2877 "id": str(uuid.uuid4()), 2878 "type": "span-update", 2879 "body": request, 2880 } 2881 2882 self.task_manager.add_task(event) 2883 except Exception as e: 2884 self.log.exception(e) 2885 finally: 2886 return StatefulSpanClient( 2887 self.client, 2888 self.id, 2889 StateType.OBSERVATION, 2890 self.trace_id, 2891 task_manager=self.task_manager, 2892 ) 2893 2894 def end( 2895 self, 2896 *, 2897 name: typing.Optional[str] = None, 2898 start_time: typing.Optional[dt.datetime] = None, 2899 end_time: typing.Optional[dt.datetime] = None, 2900 metadata: typing.Optional[typing.Any] = None, 2901 input: typing.Optional[typing.Any] = None, 2902 output: typing.Optional[typing.Any] = None, 2903 level: typing.Optional[SpanLevel] = None, 2904 status_message: typing.Optional[str] = None, 2905 version: typing.Optional[str] = None, 2906 **kwargs, 2907 ) -> "StatefulSpanClient": 2908 """End the span, optionally updating its properties. 2909 2910 Args: 2911 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 2912 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 2913 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 2914 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 2915 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2916 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 2917 input (Optional[dict]): The input to the span. Can be any JSON object. 2918 output (Optional[dict]): The output to the span. Can be any JSON object. 2919 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2920 **kwargs: Additional keyword arguments to include in the span. 2921 2922 Returns: 2923 StatefulSpanClient: The updated span. Passthrough for chaining. 2924 2925 Example: 2926 ```python 2927 from langfuse import Langfuse 2928 2929 langfuse = Langfuse() 2930 2931 # Create a trace 2932 trace = langfuse.trace(name = "llm-feature") 2933 2934 # Create a nested span in Langfuse 2935 span = trace.span(name="retrieval") 2936 2937 # End the span and update its properties 2938 span = span.end(metadata={"interface": "whatsapp"}) 2939 ``` 2940 """ 2941 try: 2942 span_body = { 2943 "name": name, 2944 "start_time": start_time, 2945 "metadata": metadata, 2946 "input": input, 2947 "output": output, 2948 "level": level, 2949 "status_message": status_message, 2950 "version": version, 2951 "end_time": end_time or _get_timestamp(), 2952 **kwargs, 2953 } 2954 return self.update(**span_body) 2955 2956 except Exception as e: 2957 self.log.warning(e) 2958 finally: 2959 return StatefulSpanClient( 2960 self.client, 2961 self.id, 2962 StateType.OBSERVATION, 2963 self.trace_id, 2964 task_manager=self.task_manager, 2965 ) 2966 2967 def get_langchain_handler(self, update_parent: bool = False): 2968 """Get langchain callback handler associated with the current span. 2969 2970 Args: 2971 update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run. 2972 2973 Returns: 2974 CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient. 2975 """ 2976 from langfuse.callback import CallbackHandler 2977 2978 return CallbackHandler( 2979 stateful_client=self, update_stateful_client=update_parent 2980 )
Class for handling stateful operations of spans in the Langfuse system. Inherits from StatefulClient.
Attributes:
- client (FernLangfuse): Core interface for Langfuse API interaction.
- id (str): Unique identifier of the span.
- state_type (StateType): Type of the stateful entity (observation or trace).
- trace_id (str): Id of trace associated with the span.
- task_manager (TaskManager): Manager for handling asynchronous tasks.
2798 def __init__( 2799 self, 2800 client: FernLangfuse, 2801 id: str, 2802 state_type: StateType, 2803 trace_id: str, 2804 task_manager: TaskManager, 2805 ): 2806 """Initialize the StatefulSpanClient.""" 2807 super().__init__(client, id, state_type, trace_id, task_manager)
Initialize the StatefulSpanClient.
2810 def update( 2811 self, 2812 *, 2813 name: typing.Optional[str] = None, 2814 start_time: typing.Optional[dt.datetime] = None, 2815 end_time: typing.Optional[dt.datetime] = None, 2816 metadata: typing.Optional[typing.Any] = None, 2817 input: typing.Optional[typing.Any] = None, 2818 output: typing.Optional[typing.Any] = None, 2819 level: typing.Optional[SpanLevel] = None, 2820 status_message: typing.Optional[str] = None, 2821 version: typing.Optional[str] = None, 2822 **kwargs, 2823 ) -> "StatefulSpanClient": 2824 """Update the span. 2825 2826 Args: 2827 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 2828 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 2829 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 2830 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 2831 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2832 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 2833 input (Optional[dict]): The input to the span. Can be any JSON object. 2834 output (Optional[dict]): The output to the span. Can be any JSON object. 2835 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2836 **kwargs: Additional keyword arguments to include in the span. 2837 2838 Returns: 2839 StatefulSpanClient: The updated span. Passthrough for chaining. 2840 2841 Example: 2842 ```python 2843 from langfuse import Langfuse 2844 2845 langfuse = Langfuse() 2846 2847 # Create a trace 2848 trace = langfuse.trace(name = "llm-feature") 2849 2850 # Create a nested span in Langfuse 2851 span = trace.span(name="retrieval") 2852 2853 # Update the span 2854 span = span.update(metadata={"interface": "whatsapp"}) 2855 ``` 2856 """ 2857 try: 2858 span_body = { 2859 "id": self.id, 2860 "trace_id": self.trace_id, # Included to avoid relying on the order of events sent to the API 2861 "name": name, 2862 "start_time": start_time, 2863 "metadata": metadata, 2864 "input": input, 2865 "output": output, 2866 "level": level, 2867 "status_message": status_message, 2868 "version": version, 2869 "end_time": end_time, 2870 **kwargs, 2871 } 2872 self.log.debug(f"Update span {_filter_io_from_event_body(span_body)}...") 2873 2874 request = UpdateSpanBody(**span_body) 2875 2876 event = { 2877 "id": str(uuid.uuid4()), 2878 "type": "span-update", 2879 "body": request, 2880 } 2881 2882 self.task_manager.add_task(event) 2883 except Exception as e: 2884 self.log.exception(e) 2885 finally: 2886 return StatefulSpanClient( 2887 self.client, 2888 self.id, 2889 StateType.OBSERVATION, 2890 self.trace_id, 2891 task_manager=self.task_manager, 2892 )
Update the span.
Arguments:
- name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
- end_time (Optional[datetime]): The time at which the span ended. Automatically set by
span.end()
. - metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
- level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. 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"})
2894 def end( 2895 self, 2896 *, 2897 name: typing.Optional[str] = None, 2898 start_time: typing.Optional[dt.datetime] = None, 2899 end_time: typing.Optional[dt.datetime] = None, 2900 metadata: typing.Optional[typing.Any] = None, 2901 input: typing.Optional[typing.Any] = None, 2902 output: typing.Optional[typing.Any] = None, 2903 level: typing.Optional[SpanLevel] = None, 2904 status_message: typing.Optional[str] = None, 2905 version: typing.Optional[str] = None, 2906 **kwargs, 2907 ) -> "StatefulSpanClient": 2908 """End the span, optionally updating its properties. 2909 2910 Args: 2911 name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI. 2912 start_time (Optional[datetime]): The time at which the span started, defaults to the current time. 2913 end_time (Optional[datetime]): The time at which the span ended. Automatically set by `span.end()`. 2914 metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API. 2915 level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be `DEBUG`, `DEFAULT`, `WARNING` or `ERROR`. Used for sorting/filtering of traces with elevated error levels and for highlighting in the UI. 2916 status_message (Optional[str]): The status message of the span. Additional field for context of the event. E.g. the error message of an error event. 2917 input (Optional[dict]): The input to the span. Can be any JSON object. 2918 output (Optional[dict]): The output to the span. Can be any JSON object. 2919 version (Optional[str]): The version of the span type. Used to understand how changes to the span type affect metrics. Useful in debugging. 2920 **kwargs: Additional keyword arguments to include in the span. 2921 2922 Returns: 2923 StatefulSpanClient: The updated span. Passthrough for chaining. 2924 2925 Example: 2926 ```python 2927 from langfuse import Langfuse 2928 2929 langfuse = Langfuse() 2930 2931 # Create a trace 2932 trace = langfuse.trace(name = "llm-feature") 2933 2934 # Create a nested span in Langfuse 2935 span = trace.span(name="retrieval") 2936 2937 # End the span and update its properties 2938 span = span.end(metadata={"interface": "whatsapp"}) 2939 ``` 2940 """ 2941 try: 2942 span_body = { 2943 "name": name, 2944 "start_time": start_time, 2945 "metadata": metadata, 2946 "input": input, 2947 "output": output, 2948 "level": level, 2949 "status_message": status_message, 2950 "version": version, 2951 "end_time": end_time or _get_timestamp(), 2952 **kwargs, 2953 } 2954 return self.update(**span_body) 2955 2956 except Exception as e: 2957 self.log.warning(e) 2958 finally: 2959 return StatefulSpanClient( 2960 self.client, 2961 self.id, 2962 StateType.OBSERVATION, 2963 self.trace_id, 2964 task_manager=self.task_manager, 2965 )
End the span, optionally updating its properties.
Arguments:
- name (Optional[str]): Identifier of the span. Useful for sorting/filtering in the UI.
- start_time (Optional[datetime]): The time at which the span started, defaults to the current time.
- end_time (Optional[datetime]): The time at which the span ended. Automatically set by
span.end()
. - metadata (Optional[dict]): Additional metadata of the span. Can be any JSON object. Metadata is merged when being updated via the API.
- level (Optional[Literal["DEBUG", "DEFAULT", "WARNING", "ERROR"]]): The level of the span. Can be
DEBUG
,DEFAULT
,WARNING
orERROR
. 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"})
2967 def get_langchain_handler(self, update_parent: bool = False): 2968 """Get langchain callback handler associated with the current span. 2969 2970 Args: 2971 update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run. 2972 2973 Returns: 2974 CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient. 2975 """ 2976 from langfuse.callback import CallbackHandler 2977 2978 return CallbackHandler( 2979 stateful_client=self, update_stateful_client=update_parent 2980 )
Get langchain callback handler associated with the current span.
Arguments:
- update_parent (bool): If set to True, the parent observation will be updated with the outcome of the Langchain run.
Returns:
CallbackHandler: An instance of CallbackHandler linked to this StatefulSpanClient.
Inherited Members
2983class StatefulTraceClient(StatefulClient): 2984 """Class for handling stateful operations of traces in the Langfuse system. Inherits from StatefulClient. 2985 2986 Attributes: 2987 client (FernLangfuse): Core interface for Langfuse API interaction. 2988 id (str): Unique identifier of the trace. 2989 state_type (StateType): Type of the stateful entity (observation or trace). 2990 trace_id (str): The trace ID associated with this client. 2991 task_manager (TaskManager): Manager for handling asynchronous tasks. 2992 """ 2993 2994 log = logging.getLogger("langfuse") 2995 2996 def __init__( 2997 self, 2998 client: FernLangfuse, 2999 id: str, 3000 state_type: StateType, 3001 trace_id: str, 3002 task_manager: TaskManager, 3003 ): 3004 """Initialize the StatefulTraceClient.""" 3005 super().__init__(client, id, state_type, trace_id, task_manager) 3006 self.task_manager = task_manager 3007 3008 def update( 3009 self, 3010 *, 3011 name: typing.Optional[str] = None, 3012 user_id: typing.Optional[str] = None, 3013 session_id: typing.Optional[str] = None, 3014 version: typing.Optional[str] = None, 3015 release: typing.Optional[str] = None, 3016 input: typing.Optional[typing.Any] = None, 3017 output: typing.Optional[typing.Any] = None, 3018 metadata: typing.Optional[typing.Any] = None, 3019 tags: typing.Optional[typing.List[str]] = None, 3020 public: typing.Optional[bool] = None, 3021 **kwargs, 3022 ) -> "StatefulTraceClient": 3023 """Update the trace. 3024 3025 Args: 3026 name: Identifier of the trace. Useful for sorting/filtering in the UI. 3027 input: The input of the trace. Can be any JSON object. 3028 output: The output of the trace. Can be any JSON object. 3029 metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API. 3030 user_id: The id of the user that triggered the execution. Used to provide user-level analytics. 3031 session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier. 3032 version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging. 3033 release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging. 3034 tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API. 3035 public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project. 3036 **kwargs: Additional keyword arguments that can be included in the trace. 3037 3038 Returns: 3039 StatefulTraceClient: The updated trace. Passthrough for chaining. 3040 3041 Example: 3042 ```python 3043 from langfuse import Langfuse 3044 3045 langfuse = Langfuse() 3046 3047 # Create a trace 3048 trace = langfuse.trace( 3049 name="example-application", 3050 user_id="user-1234") 3051 ) 3052 3053 # Update the trace 3054 trace = trace.update( 3055 output={"result": "success"}, 3056 metadata={"interface": "whatsapp"} 3057 ) 3058 ``` 3059 """ 3060 try: 3061 trace_body = { 3062 "id": self.id, 3063 "name": name, 3064 "userId": user_id, 3065 "sessionId": session_id 3066 or kwargs.get("sessionId", None), # backward compatibility 3067 "version": version, 3068 "release": release, 3069 "input": input, 3070 "output": output, 3071 "metadata": metadata, 3072 "public": public, 3073 "tags": tags, 3074 **kwargs, 3075 } 3076 self.log.debug(f"Update trace {_filter_io_from_event_body(trace_body)}...") 3077 3078 request = TraceBody(**trace_body) 3079 3080 event = { 3081 "id": str(uuid.uuid4()), 3082 "type": "trace-create", 3083 "body": request, 3084 } 3085 3086 self.task_manager.add_task(event) 3087 3088 except Exception as e: 3089 self.log.exception(e) 3090 finally: 3091 return StatefulTraceClient( 3092 self.client, 3093 self.id, 3094 StateType.TRACE, 3095 self.trace_id, 3096 task_manager=self.task_manager, 3097 ) 3098 3099 def get_langchain_handler(self, update_parent: bool = False): 3100 """Get langchain callback handler associated with the current trace. 3101 3102 This method creates and returns a CallbackHandler instance, linking it with the current 3103 trace. Use this if you want to group multiple Langchain runs within a single trace. 3104 3105 Args: 3106 update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run. 3107 3108 Raises: 3109 ImportError: If the 'langchain' module is not installed, indicating missing functionality. 3110 3111 Returns: 3112 CallbackHandler: Langchain callback handler linked to the current trace. 3113 3114 Example: 3115 ```python 3116 from langfuse import Langfuse 3117 3118 langfuse = Langfuse() 3119 3120 # Create a trace 3121 trace = langfuse.trace(name = "llm-feature") 3122 3123 # Get a langchain callback handler 3124 handler = trace.get_langchain_handler() 3125 ``` 3126 """ 3127 try: 3128 from langfuse.callback import CallbackHandler 3129 3130 self.log.debug(f"Creating new handler for trace {self.id}") 3131 3132 return CallbackHandler( 3133 stateful_client=self, 3134 debug=self.log.level == logging.DEBUG, 3135 update_stateful_client=update_parent, 3136 ) 3137 except Exception as e: 3138 self.log.exception(e) 3139 3140 def getNewHandler(self): 3141 """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated.""" 3142 return self.get_langchain_handler()
Class for handling stateful operations of traces in the Langfuse system. Inherits from StatefulClient.
Attributes:
- client (FernLangfuse): Core interface for Langfuse API interaction.
- id (str): Unique identifier of the trace.
- state_type (StateType): Type of the stateful entity (observation or trace).
- trace_id (str): The trace ID associated with this client.
- task_manager (TaskManager): Manager for handling asynchronous tasks.
2996 def __init__( 2997 self, 2998 client: FernLangfuse, 2999 id: str, 3000 state_type: StateType, 3001 trace_id: str, 3002 task_manager: TaskManager, 3003 ): 3004 """Initialize the StatefulTraceClient.""" 3005 super().__init__(client, id, state_type, trace_id, task_manager) 3006 self.task_manager = task_manager
Initialize the StatefulTraceClient.
3008 def update( 3009 self, 3010 *, 3011 name: typing.Optional[str] = None, 3012 user_id: typing.Optional[str] = None, 3013 session_id: typing.Optional[str] = None, 3014 version: typing.Optional[str] = None, 3015 release: typing.Optional[str] = None, 3016 input: typing.Optional[typing.Any] = None, 3017 output: typing.Optional[typing.Any] = None, 3018 metadata: typing.Optional[typing.Any] = None, 3019 tags: typing.Optional[typing.List[str]] = None, 3020 public: typing.Optional[bool] = None, 3021 **kwargs, 3022 ) -> "StatefulTraceClient": 3023 """Update the trace. 3024 3025 Args: 3026 name: Identifier of the trace. Useful for sorting/filtering in the UI. 3027 input: The input of the trace. Can be any JSON object. 3028 output: The output of the trace. Can be any JSON object. 3029 metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API. 3030 user_id: The id of the user that triggered the execution. Used to provide user-level analytics. 3031 session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier. 3032 version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging. 3033 release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging. 3034 tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API. 3035 public: You can make a trace `public` to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project. 3036 **kwargs: Additional keyword arguments that can be included in the trace. 3037 3038 Returns: 3039 StatefulTraceClient: The updated trace. Passthrough for chaining. 3040 3041 Example: 3042 ```python 3043 from langfuse import Langfuse 3044 3045 langfuse = Langfuse() 3046 3047 # Create a trace 3048 trace = langfuse.trace( 3049 name="example-application", 3050 user_id="user-1234") 3051 ) 3052 3053 # Update the trace 3054 trace = trace.update( 3055 output={"result": "success"}, 3056 metadata={"interface": "whatsapp"} 3057 ) 3058 ``` 3059 """ 3060 try: 3061 trace_body = { 3062 "id": self.id, 3063 "name": name, 3064 "userId": user_id, 3065 "sessionId": session_id 3066 or kwargs.get("sessionId", None), # backward compatibility 3067 "version": version, 3068 "release": release, 3069 "input": input, 3070 "output": output, 3071 "metadata": metadata, 3072 "public": public, 3073 "tags": tags, 3074 **kwargs, 3075 } 3076 self.log.debug(f"Update trace {_filter_io_from_event_body(trace_body)}...") 3077 3078 request = TraceBody(**trace_body) 3079 3080 event = { 3081 "id": str(uuid.uuid4()), 3082 "type": "trace-create", 3083 "body": request, 3084 } 3085 3086 self.task_manager.add_task(event) 3087 3088 except Exception as e: 3089 self.log.exception(e) 3090 finally: 3091 return StatefulTraceClient( 3092 self.client, 3093 self.id, 3094 StateType.TRACE, 3095 self.trace_id, 3096 task_manager=self.task_manager, 3097 )
Update the trace.
Arguments:
- name: Identifier of the trace. Useful for sorting/filtering in the UI.
- input: The input of the trace. Can be any JSON object.
- output: The output of the trace. Can be any JSON object.
- metadata: Additional metadata of the trace. Can be any JSON object. Metadata is merged when being updated via the API.
- user_id: The id of the user that triggered the execution. Used to provide user-level analytics.
- session_id: Used to group multiple traces into a session in Langfuse. Use your own session/thread identifier.
- version: The version of the trace type. Used to understand how changes to the trace type affect metrics. Useful in debugging.
- release: The release identifier of the current deployment. Used to understand how changes of different deployments affect metrics. Useful in debugging.
- tags: Tags are used to categorize or label traces. Traces can be filtered by tags in the UI and GET API. Tags can also be changed in the UI. Tags are merged and never deleted via the API.
- public: You can make a trace
public
to share it via a public link. This allows others to view the trace without needing to log in or be members of your Langfuse project. - **kwargs: Additional keyword arguments that can be included in the trace.
Returns:
StatefulTraceClient: The updated trace. Passthrough for chaining.
Example:
from langfuse import Langfuse langfuse = Langfuse() # Create a trace trace = langfuse.trace( name="example-application", user_id="user-1234") ) # Update the trace trace = trace.update( output={"result": "success"}, metadata={"interface": "whatsapp"} )
3099 def get_langchain_handler(self, update_parent: bool = False): 3100 """Get langchain callback handler associated with the current trace. 3101 3102 This method creates and returns a CallbackHandler instance, linking it with the current 3103 trace. Use this if you want to group multiple Langchain runs within a single trace. 3104 3105 Args: 3106 update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run. 3107 3108 Raises: 3109 ImportError: If the 'langchain' module is not installed, indicating missing functionality. 3110 3111 Returns: 3112 CallbackHandler: Langchain callback handler linked to the current trace. 3113 3114 Example: 3115 ```python 3116 from langfuse import Langfuse 3117 3118 langfuse = Langfuse() 3119 3120 # Create a trace 3121 trace = langfuse.trace(name = "llm-feature") 3122 3123 # Get a langchain callback handler 3124 handler = trace.get_langchain_handler() 3125 ``` 3126 """ 3127 try: 3128 from langfuse.callback import CallbackHandler 3129 3130 self.log.debug(f"Creating new handler for trace {self.id}") 3131 3132 return CallbackHandler( 3133 stateful_client=self, 3134 debug=self.log.level == logging.DEBUG, 3135 update_stateful_client=update_parent, 3136 ) 3137 except Exception as e: 3138 self.log.exception(e)
Get langchain callback handler associated with the current trace.
This method creates and returns a CallbackHandler instance, linking it with the current trace. Use this if you want to group multiple Langchain runs within a single trace.
Arguments:
- update_parent (bool): If set to True, the parent trace will be updated with the outcome of the Langchain run.
Raises:
- ImportError: If the 'langchain' module is not installed, indicating missing functionality.
Returns:
CallbackHandler: Langchain callback handler linked to the current trace.
Example:
from langfuse import Langfuse langfuse = Langfuse() # Create a trace trace = langfuse.trace(name = "llm-feature") # Get a langchain callback handler handler = trace.get_langchain_handler()
3140 def getNewHandler(self): 3141 """Alias for the `get_langchain_handler` method. Retrieves a callback handler for the trace. Deprecated.""" 3142 return self.get_langchain_handler()
Alias for the get_langchain_handler
method. Retrieves a callback handler for the trace. Deprecated.
Inherited Members
3145class DatasetItemClient: 3146 """Class for managing dataset items in Langfuse. 3147 3148 Args: 3149 id (str): Unique identifier of the dataset item. 3150 status (DatasetStatus): The status of the dataset item. Can be either 'ACTIVE' or 'ARCHIVED'. 3151 input (Any): Input data of the dataset item. 3152 expected_output (Optional[Any]): Expected output of the dataset item. 3153 metadata (Optional[Any]): Additional metadata of the dataset item. 3154 source_trace_id (Optional[str]): Identifier of the source trace. 3155 source_observation_id (Optional[str]): Identifier of the source observation. 3156 dataset_id (str): Identifier of the dataset to which this item belongs. 3157 dataset_name (str): Name of the dataset to which this item belongs. 3158 created_at (datetime): Timestamp of dataset item creation. 3159 updated_at (datetime): Timestamp of the last update to the dataset item. 3160 langfuse (Langfuse): Instance of Langfuse client for API interactions. 3161 3162 Example: 3163 ```python 3164 from langfuse import Langfuse 3165 3166 langfuse = Langfuse() 3167 3168 dataset = langfuse.get_dataset("<dataset_name>") 3169 3170 for item in dataset.items: 3171 # Generate a completion using the input of every item 3172 completion, generation = llm_app.run(item.input) 3173 3174 # Evaluate the completion 3175 generation.score( 3176 name="example-score", 3177 value=1 3178 ) 3179 ``` 3180 """ 3181 3182 log = logging.getLogger("langfuse") 3183 3184 id: str 3185 status: DatasetStatus 3186 input: typing.Any 3187 expected_output: typing.Optional[typing.Any] 3188 metadata: Optional[Any] 3189 source_trace_id: typing.Optional[str] 3190 source_observation_id: typing.Optional[str] 3191 dataset_id: str 3192 dataset_name: str 3193 created_at: dt.datetime 3194 updated_at: dt.datetime 3195 3196 langfuse: Langfuse 3197 3198 def __init__(self, dataset_item: DatasetItem, langfuse: Langfuse): 3199 """Initialize the DatasetItemClient.""" 3200 self.id = dataset_item.id 3201 self.status = dataset_item.status 3202 self.input = dataset_item.input 3203 self.expected_output = dataset_item.expected_output 3204 self.metadata = dataset_item.metadata 3205 self.source_trace_id = dataset_item.source_trace_id 3206 self.source_observation_id = dataset_item.source_observation_id 3207 self.dataset_id = dataset_item.dataset_id 3208 self.dataset_name = dataset_item.dataset_name 3209 self.created_at = dataset_item.created_at 3210 self.updated_at = dataset_item.updated_at 3211 3212 self.langfuse = langfuse 3213 3214 def flush(self, observation: StatefulClient, run_name: str): 3215 """Flushes an observations task manager's queue. 3216 3217 Used before creating a dataset run item to ensure all events are persistent. 3218 3219 Args: 3220 observation (StatefulClient): The observation or trace client associated with the dataset item. 3221 run_name (str): The name of the dataset run. 3222 """ 3223 observation.task_manager.flush() 3224 3225 def link( 3226 self, 3227 trace_or_observation: typing.Union[StatefulClient, str, None], 3228 run_name: str, 3229 run_metadata: Optional[Any] = None, 3230 run_description: Optional[str] = None, 3231 trace_id: Optional[str] = None, 3232 observation_id: Optional[str] = None, 3233 ): 3234 """Link the dataset item to observation within a specific dataset run. Creates a dataset run item. 3235 3236 Args: 3237 trace_or_observation (Union[StatefulClient, str, None]): The trace or observation object to link. Deprecated: can also be an observation ID. 3238 run_name (str): The name of the dataset run. 3239 run_metadata (Optional[Any]): Additional metadata to include in dataset run. 3240 run_description (Optional[str]): Description of the dataset run. 3241 trace_id (Optional[str]): The trace ID to link to the dataset item. Set trace_or_observation to None if trace_id is provided. 3242 observation_id (Optional[str]): The observation ID to link to the dataset item (optional). Set trace_or_observation to None if trace_id is provided. 3243 """ 3244 parsed_trace_id: str = None 3245 parsed_observation_id: str = None 3246 3247 if isinstance(trace_or_observation, StatefulClient): 3248 # flush the queue before creating the dataset run item 3249 # to ensure that all events are persisted. 3250 if trace_or_observation.state_type == StateType.TRACE: 3251 parsed_trace_id = trace_or_observation.trace_id 3252 elif trace_or_observation.state_type == StateType.OBSERVATION: 3253 parsed_observation_id = trace_or_observation.id 3254 parsed_trace_id = trace_or_observation.trace_id 3255 # legacy support for observation_id 3256 elif isinstance(trace_or_observation, str): 3257 parsed_observation_id = trace_or_observation 3258 elif trace_or_observation is None: 3259 if trace_id is not None: 3260 parsed_trace_id = trace_id 3261 if observation_id is not None: 3262 parsed_observation_id = observation_id 3263 else: 3264 raise ValueError( 3265 "trace_id must be provided if trace_or_observation is None" 3266 ) 3267 else: 3268 raise ValueError( 3269 "trace_or_observation (arg) or trace_id (kwarg) must be provided to link the dataset item" 3270 ) 3271 3272 self.log.debug( 3273 f"Creating dataset run item: {run_name} {self.id} {parsed_trace_id} {parsed_observation_id}" 3274 ) 3275 self.langfuse.client.dataset_run_items.create( 3276 request=CreateDatasetRunItemRequest( 3277 runName=run_name, 3278 datasetItemId=self.id, 3279 traceId=parsed_trace_id, 3280 observationId=parsed_observation_id, 3281 metadata=run_metadata, 3282 runDescription=run_description, 3283 ) 3284 ) 3285 3286 def get_langchain_handler( 3287 self, 3288 *, 3289 run_name: str, 3290 run_description: Optional[str] = None, 3291 run_metadata: Optional[Any] = None, 3292 ): 3293 """Create and get a langchain callback handler linked to this dataset item. 3294 3295 Args: 3296 run_name (str): The name of the dataset run to be used in the callback handler. 3297 run_description (Optional[str]): Description of the dataset run. 3298 run_metadata (Optional[Any]): Additional metadata to include in dataset run. 3299 3300 Returns: 3301 CallbackHandler: An instance of CallbackHandler linked to the dataset item. 3302 """ 3303 metadata = { 3304 "dataset_item_id": self.id, 3305 "run_name": run_name, 3306 "dataset_id": self.dataset_id, 3307 } 3308 trace = self.langfuse.trace(name="dataset-run", metadata=metadata) 3309 3310 self.link( 3311 trace, run_name, run_metadata=run_metadata, run_description=run_description 3312 ) 3313 3314 return trace.get_langchain_handler(update_parent=True) 3315 3316 @contextmanager 3317 def observe( 3318 self, 3319 *, 3320 run_name: str, 3321 run_description: Optional[str] = None, 3322 run_metadata: Optional[Any] = None, 3323 trace_id: Optional[str] = None, 3324 ): 3325 """Observes a dataset run within the Langfuse client. 3326 3327 Args: 3328 run_name (str): The name of the dataset run. 3329 root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created. 3330 run_description (Optional[str]): The description of the dataset run. 3331 run_metadata (Optional[Any]): Additional metadata for the dataset run. 3332 3333 Yields: 3334 StatefulTraceClient: The trace associated with the dataset run. 3335 """ 3336 from langfuse.decorators import langfuse_context 3337 3338 root_trace_id = trace_id or str(uuid.uuid4()) 3339 3340 langfuse_context._set_root_trace_id(root_trace_id) 3341 3342 try: 3343 yield root_trace_id 3344 3345 finally: 3346 self.link( 3347 run_name=run_name, 3348 run_metadata=run_metadata, 3349 run_description=run_description, 3350 trace_or_observation=None, 3351 trace_id=root_trace_id, 3352 ) 3353 3354 @contextmanager 3355 def observe_llama_index( 3356 self, 3357 *, 3358 run_name: str, 3359 run_description: Optional[str] = None, 3360 run_metadata: Optional[Any] = None, 3361 llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {}, 3362 ): 3363 """Context manager for observing LlamaIndex operations linked to this dataset item. 3364 3365 This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging 3366 and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all 3367 operations performed within the context are linked to the appropriate dataset item and run in Langfuse. 3368 3369 Args: 3370 run_name (str): The name of the dataset run. 3371 run_description (Optional[str]): Description of the dataset run. Defaults to None. 3372 run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None. 3373 llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass 3374 to the LlamaIndex integration constructor. Defaults to an empty dictionary. 3375 3376 Yields: 3377 LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations. 3378 3379 Example: 3380 ```python 3381 dataset_item = dataset.items[0] 3382 3383 with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler: 3384 # Perform LlamaIndex operations here 3385 some_llama_index_operation() 3386 ``` 3387 3388 Raises: 3389 ImportError: If required modules for LlamaIndex integration are not available. 3390 """ 3391 metadata = { 3392 "dataset_item_id": self.id, 3393 "run_name": run_name, 3394 "dataset_id": self.dataset_id, 3395 } 3396 trace = self.langfuse.trace(name="dataset-run", metadata=metadata) 3397 self.link( 3398 trace, run_name, run_metadata=run_metadata, run_description=run_description 3399 ) 3400 3401 try: 3402 import llama_index.core 3403 from llama_index.core import Settings 3404 from llama_index.core.callbacks import CallbackManager 3405 3406 from langfuse.llama_index import LlamaIndexCallbackHandler 3407 3408 callback_handler = LlamaIndexCallbackHandler( 3409 **llama_index_integration_constructor_kwargs, 3410 ) 3411 callback_handler.set_root(trace, update_root=True) 3412 3413 # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler 3414 # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it 3415 prev_global_handler = llama_index.core.global_handler 3416 prev_langfuse_handler = None 3417 3418 if isinstance(prev_global_handler, LlamaIndexCallbackHandler): 3419 llama_index.core.global_handler = None 3420 3421 if Settings.callback_manager is None: 3422 Settings.callback_manager = CallbackManager([callback_handler]) 3423 else: 3424 for handler in Settings.callback_manager.handlers: 3425 if isinstance(handler, LlamaIndexCallbackHandler): 3426 prev_langfuse_handler = handler 3427 Settings.callback_manager.remove_handler(handler) 3428 3429 Settings.callback_manager.add_handler(callback_handler) 3430 3431 except Exception as e: 3432 self.log.exception(e) 3433 3434 try: 3435 yield callback_handler 3436 finally: 3437 # Reset the handlers 3438 Settings.callback_manager.remove_handler(callback_handler) 3439 if prev_langfuse_handler is not None: 3440 Settings.callback_manager.add_handler(prev_langfuse_handler) 3441 3442 llama_index.core.global_handler = prev_global_handler 3443 3444 def get_llama_index_handler( 3445 self, 3446 *, 3447 run_name: str, 3448 run_description: Optional[str] = None, 3449 run_metadata: Optional[Any] = None, 3450 llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {}, 3451 ): 3452 """Create and get a llama-index callback handler linked to this dataset item. 3453 3454 Args: 3455 run_name (str): The name of the dataset run to be used in the callback handler. 3456 run_description (Optional[str]): Description of the dataset run. 3457 run_metadata (Optional[Any]): Additional metadata to include in dataset run. 3458 llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor. 3459 3460 Returns: 3461 LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item. 3462 """ 3463 metadata = { 3464 "dataset_item_id": self.id, 3465 "run_name": run_name, 3466 "dataset_id": self.dataset_id, 3467 } 3468 trace = self.langfuse.trace(name="dataset-run", metadata=metadata) 3469 3470 self.link( 3471 trace, run_name, run_metadata=run_metadata, run_description=run_description 3472 ) 3473 3474 try: 3475 from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler 3476 3477 callback_handler = LlamaIndexCallbackHandler( 3478 **llama_index_integration_constructor_kwargs, 3479 ) 3480 callback_handler.set_root(trace, update_root=True) 3481 3482 return callback_handler 3483 except Exception as e: 3484 self.log.exception(e)
Class for managing dataset items in Langfuse.
Arguments:
- id (str): Unique identifier of the dataset item.
- status (DatasetStatus): The status of the dataset item. Can be either 'ACTIVE' or 'ARCHIVED'.
- input (Any): Input data of the dataset item.
- expected_output (Optional[Any]): Expected output of the dataset item.
- metadata (Optional[Any]): Additional metadata of the dataset item.
- source_trace_id (Optional[str]): Identifier of the source trace.
- source_observation_id (Optional[str]): Identifier of the source observation.
- dataset_id (str): Identifier of the dataset to which this item belongs.
- dataset_name (str): Name of the dataset to which this item belongs.
- created_at (datetime): Timestamp of dataset item creation.
- updated_at (datetime): Timestamp of the last update to the dataset item.
- langfuse (Langfuse): Instance of Langfuse client for API interactions.
Example:
from langfuse import Langfuse langfuse = Langfuse() dataset = langfuse.get_dataset("<dataset_name>") for item in dataset.items: # Generate a completion using the input of every item completion, generation = llm_app.run(item.input) # Evaluate the completion generation.score( name="example-score", value=1 )
3198 def __init__(self, dataset_item: DatasetItem, langfuse: Langfuse): 3199 """Initialize the DatasetItemClient.""" 3200 self.id = dataset_item.id 3201 self.status = dataset_item.status 3202 self.input = dataset_item.input 3203 self.expected_output = dataset_item.expected_output 3204 self.metadata = dataset_item.metadata 3205 self.source_trace_id = dataset_item.source_trace_id 3206 self.source_observation_id = dataset_item.source_observation_id 3207 self.dataset_id = dataset_item.dataset_id 3208 self.dataset_name = dataset_item.dataset_name 3209 self.created_at = dataset_item.created_at 3210 self.updated_at = dataset_item.updated_at 3211 3212 self.langfuse = langfuse
Initialize the DatasetItemClient.
3214 def flush(self, observation: StatefulClient, run_name: str): 3215 """Flushes an observations task manager's queue. 3216 3217 Used before creating a dataset run item to ensure all events are persistent. 3218 3219 Args: 3220 observation (StatefulClient): The observation or trace client associated with the dataset item. 3221 run_name (str): The name of the dataset run. 3222 """ 3223 observation.task_manager.flush()
Flushes an observations task manager's queue.
Used before creating a dataset run item to ensure all events are persistent.
Arguments:
- observation (StatefulClient): The observation or trace client associated with the dataset item.
- run_name (str): The name of the dataset run.
3225 def link( 3226 self, 3227 trace_or_observation: typing.Union[StatefulClient, str, None], 3228 run_name: str, 3229 run_metadata: Optional[Any] = None, 3230 run_description: Optional[str] = None, 3231 trace_id: Optional[str] = None, 3232 observation_id: Optional[str] = None, 3233 ): 3234 """Link the dataset item to observation within a specific dataset run. Creates a dataset run item. 3235 3236 Args: 3237 trace_or_observation (Union[StatefulClient, str, None]): The trace or observation object to link. Deprecated: can also be an observation ID. 3238 run_name (str): The name of the dataset run. 3239 run_metadata (Optional[Any]): Additional metadata to include in dataset run. 3240 run_description (Optional[str]): Description of the dataset run. 3241 trace_id (Optional[str]): The trace ID to link to the dataset item. Set trace_or_observation to None if trace_id is provided. 3242 observation_id (Optional[str]): The observation ID to link to the dataset item (optional). Set trace_or_observation to None if trace_id is provided. 3243 """ 3244 parsed_trace_id: str = None 3245 parsed_observation_id: str = None 3246 3247 if isinstance(trace_or_observation, StatefulClient): 3248 # flush the queue before creating the dataset run item 3249 # to ensure that all events are persisted. 3250 if trace_or_observation.state_type == StateType.TRACE: 3251 parsed_trace_id = trace_or_observation.trace_id 3252 elif trace_or_observation.state_type == StateType.OBSERVATION: 3253 parsed_observation_id = trace_or_observation.id 3254 parsed_trace_id = trace_or_observation.trace_id 3255 # legacy support for observation_id 3256 elif isinstance(trace_or_observation, str): 3257 parsed_observation_id = trace_or_observation 3258 elif trace_or_observation is None: 3259 if trace_id is not None: 3260 parsed_trace_id = trace_id 3261 if observation_id is not None: 3262 parsed_observation_id = observation_id 3263 else: 3264 raise ValueError( 3265 "trace_id must be provided if trace_or_observation is None" 3266 ) 3267 else: 3268 raise ValueError( 3269 "trace_or_observation (arg) or trace_id (kwarg) must be provided to link the dataset item" 3270 ) 3271 3272 self.log.debug( 3273 f"Creating dataset run item: {run_name} {self.id} {parsed_trace_id} {parsed_observation_id}" 3274 ) 3275 self.langfuse.client.dataset_run_items.create( 3276 request=CreateDatasetRunItemRequest( 3277 runName=run_name, 3278 datasetItemId=self.id, 3279 traceId=parsed_trace_id, 3280 observationId=parsed_observation_id, 3281 metadata=run_metadata, 3282 runDescription=run_description, 3283 ) 3284 )
Link the dataset item to observation within a specific dataset run. Creates a dataset run item.
Arguments:
- trace_or_observation (Union[StatefulClient, str, None]): The trace or observation object to link. Deprecated: can also be an observation ID.
- run_name (str): The name of the dataset run.
- run_metadata (Optional[Any]): Additional metadata to include in dataset run.
- run_description (Optional[str]): Description of the dataset run.
- trace_id (Optional[str]): The trace ID to link to the dataset item. Set trace_or_observation to None if trace_id is provided.
- 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.
3286 def get_langchain_handler( 3287 self, 3288 *, 3289 run_name: str, 3290 run_description: Optional[str] = None, 3291 run_metadata: Optional[Any] = None, 3292 ): 3293 """Create and get a langchain callback handler linked to this dataset item. 3294 3295 Args: 3296 run_name (str): The name of the dataset run to be used in the callback handler. 3297 run_description (Optional[str]): Description of the dataset run. 3298 run_metadata (Optional[Any]): Additional metadata to include in dataset run. 3299 3300 Returns: 3301 CallbackHandler: An instance of CallbackHandler linked to the dataset item. 3302 """ 3303 metadata = { 3304 "dataset_item_id": self.id, 3305 "run_name": run_name, 3306 "dataset_id": self.dataset_id, 3307 } 3308 trace = self.langfuse.trace(name="dataset-run", metadata=metadata) 3309 3310 self.link( 3311 trace, run_name, run_metadata=run_metadata, run_description=run_description 3312 ) 3313 3314 return trace.get_langchain_handler(update_parent=True)
Create and get a langchain callback handler linked to this dataset item.
Arguments:
- run_name (str): The name of the dataset run to be used in the callback handler.
- run_description (Optional[str]): Description of the dataset run.
- run_metadata (Optional[Any]): Additional metadata to include in dataset run.
Returns:
CallbackHandler: An instance of CallbackHandler linked to the dataset item.
3316 @contextmanager 3317 def observe( 3318 self, 3319 *, 3320 run_name: str, 3321 run_description: Optional[str] = None, 3322 run_metadata: Optional[Any] = None, 3323 trace_id: Optional[str] = None, 3324 ): 3325 """Observes a dataset run within the Langfuse client. 3326 3327 Args: 3328 run_name (str): The name of the dataset run. 3329 root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created. 3330 run_description (Optional[str]): The description of the dataset run. 3331 run_metadata (Optional[Any]): Additional metadata for the dataset run. 3332 3333 Yields: 3334 StatefulTraceClient: The trace associated with the dataset run. 3335 """ 3336 from langfuse.decorators import langfuse_context 3337 3338 root_trace_id = trace_id or str(uuid.uuid4()) 3339 3340 langfuse_context._set_root_trace_id(root_trace_id) 3341 3342 try: 3343 yield root_trace_id 3344 3345 finally: 3346 self.link( 3347 run_name=run_name, 3348 run_metadata=run_metadata, 3349 run_description=run_description, 3350 trace_or_observation=None, 3351 trace_id=root_trace_id, 3352 )
Observes a dataset run within the Langfuse client.
Arguments:
- run_name (str): The name of the dataset run.
- root_trace (Optional[StatefulTraceClient]): The root trace client to use for the dataset run. If not provided, a new trace client will be created.
- run_description (Optional[str]): The description of the dataset run.
- run_metadata (Optional[Any]): Additional metadata for the dataset run.
Yields:
StatefulTraceClient: The trace associated with the dataset run.
3354 @contextmanager 3355 def observe_llama_index( 3356 self, 3357 *, 3358 run_name: str, 3359 run_description: Optional[str] = None, 3360 run_metadata: Optional[Any] = None, 3361 llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {}, 3362 ): 3363 """Context manager for observing LlamaIndex operations linked to this dataset item. 3364 3365 This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging 3366 and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all 3367 operations performed within the context are linked to the appropriate dataset item and run in Langfuse. 3368 3369 Args: 3370 run_name (str): The name of the dataset run. 3371 run_description (Optional[str]): Description of the dataset run. Defaults to None. 3372 run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None. 3373 llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass 3374 to the LlamaIndex integration constructor. Defaults to an empty dictionary. 3375 3376 Yields: 3377 LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations. 3378 3379 Example: 3380 ```python 3381 dataset_item = dataset.items[0] 3382 3383 with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler: 3384 # Perform LlamaIndex operations here 3385 some_llama_index_operation() 3386 ``` 3387 3388 Raises: 3389 ImportError: If required modules for LlamaIndex integration are not available. 3390 """ 3391 metadata = { 3392 "dataset_item_id": self.id, 3393 "run_name": run_name, 3394 "dataset_id": self.dataset_id, 3395 } 3396 trace = self.langfuse.trace(name="dataset-run", metadata=metadata) 3397 self.link( 3398 trace, run_name, run_metadata=run_metadata, run_description=run_description 3399 ) 3400 3401 try: 3402 import llama_index.core 3403 from llama_index.core import Settings 3404 from llama_index.core.callbacks import CallbackManager 3405 3406 from langfuse.llama_index import LlamaIndexCallbackHandler 3407 3408 callback_handler = LlamaIndexCallbackHandler( 3409 **llama_index_integration_constructor_kwargs, 3410 ) 3411 callback_handler.set_root(trace, update_root=True) 3412 3413 # Temporarily set the global handler to the new handler if previous handler is a LlamaIndexCallbackHandler 3414 # LlamaIndex does not adding two errors of same type, so if global handler is already a LlamaIndexCallbackHandler, we need to remove it 3415 prev_global_handler = llama_index.core.global_handler 3416 prev_langfuse_handler = None 3417 3418 if isinstance(prev_global_handler, LlamaIndexCallbackHandler): 3419 llama_index.core.global_handler = None 3420 3421 if Settings.callback_manager is None: 3422 Settings.callback_manager = CallbackManager([callback_handler]) 3423 else: 3424 for handler in Settings.callback_manager.handlers: 3425 if isinstance(handler, LlamaIndexCallbackHandler): 3426 prev_langfuse_handler = handler 3427 Settings.callback_manager.remove_handler(handler) 3428 3429 Settings.callback_manager.add_handler(callback_handler) 3430 3431 except Exception as e: 3432 self.log.exception(e) 3433 3434 try: 3435 yield callback_handler 3436 finally: 3437 # Reset the handlers 3438 Settings.callback_manager.remove_handler(callback_handler) 3439 if prev_langfuse_handler is not None: 3440 Settings.callback_manager.add_handler(prev_langfuse_handler) 3441 3442 llama_index.core.global_handler = prev_global_handler
Context manager for observing LlamaIndex operations linked to this dataset item.
This method sets up a LlamaIndex callback handler that integrates with Langfuse, allowing detailed logging and tracing of LlamaIndex operations within the context of a specific dataset run. It ensures that all operations performed within the context are linked to the appropriate dataset item and run in Langfuse.
Arguments:
- run_name (str): The name of the dataset run.
- run_description (Optional[str]): Description of the dataset run. Defaults to None.
- run_metadata (Optional[Any]): Additional metadata for the dataset run. Defaults to None.
- llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Keyword arguments to pass to the LlamaIndex integration constructor. Defaults to an empty dictionary.
Yields:
LlamaIndexCallbackHandler: The callback handler for LlamaIndex operations.
Example:
dataset_item = dataset.items[0] with dataset_item.observe_llama_index(run_name="example-run", run_description="Example LlamaIndex run") as handler: # Perform LlamaIndex operations here some_llama_index_operation()
Raises:
- ImportError: If required modules for LlamaIndex integration are not available.
3444 def get_llama_index_handler( 3445 self, 3446 *, 3447 run_name: str, 3448 run_description: Optional[str] = None, 3449 run_metadata: Optional[Any] = None, 3450 llama_index_integration_constructor_kwargs: Optional[Dict[str, Any]] = {}, 3451 ): 3452 """Create and get a llama-index callback handler linked to this dataset item. 3453 3454 Args: 3455 run_name (str): The name of the dataset run to be used in the callback handler. 3456 run_description (Optional[str]): Description of the dataset run. 3457 run_metadata (Optional[Any]): Additional metadata to include in dataset run. 3458 llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor. 3459 3460 Returns: 3461 LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item. 3462 """ 3463 metadata = { 3464 "dataset_item_id": self.id, 3465 "run_name": run_name, 3466 "dataset_id": self.dataset_id, 3467 } 3468 trace = self.langfuse.trace(name="dataset-run", metadata=metadata) 3469 3470 self.link( 3471 trace, run_name, run_metadata=run_metadata, run_description=run_description 3472 ) 3473 3474 try: 3475 from langfuse.llama_index.llama_index import LlamaIndexCallbackHandler 3476 3477 callback_handler = LlamaIndexCallbackHandler( 3478 **llama_index_integration_constructor_kwargs, 3479 ) 3480 callback_handler.set_root(trace, update_root=True) 3481 3482 return callback_handler 3483 except Exception as e: 3484 self.log.exception(e)
Create and get a llama-index callback handler linked to this dataset item.
Arguments:
- run_name (str): The name of the dataset run to be used in the callback handler.
- run_description (Optional[str]): Description of the dataset run.
- run_metadata (Optional[Any]): Additional metadata to include in dataset run.
- llama_index_integration_constructor_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments to pass to the LlamaIndex integration constructor.
Returns:
LlamaIndexCallbackHandler: An instance of LlamaIndexCallbackHandler linked to the dataset item.
3487class DatasetClient: 3488 """Class for managing datasets in Langfuse. 3489 3490 Attributes: 3491 id (str): Unique identifier of the dataset. 3492 name (str): Name of the dataset. 3493 description (Optional[str]): Description of the dataset. 3494 metadata (Optional[typing.Any]): Additional metadata of the dataset. 3495 project_id (str): Identifier of the project to which the dataset belongs. 3496 dataset_name (str): Name of the dataset. 3497 created_at (datetime): Timestamp of dataset creation. 3498 updated_at (datetime): Timestamp of the last update to the dataset. 3499 items (List[DatasetItemClient]): List of dataset items associated with the dataset. 3500 runs (List[str]): List of dataset runs associated with the dataset. Deprecated. 3501 3502 Example: 3503 Print the input of each dataset item in a dataset. 3504 ```python 3505 from langfuse import Langfuse 3506 3507 langfuse = Langfuse() 3508 3509 dataset = langfuse.get_dataset("<dataset_name>") 3510 3511 for item in dataset.items: 3512 print(item.input) 3513 ``` 3514 """ 3515 3516 id: str 3517 name: str 3518 description: Optional[str] 3519 project_id: str 3520 dataset_name: str # for backward compatibility, to be deprecated 3521 metadata: Optional[Any] 3522 created_at: dt.datetime 3523 updated_at: dt.datetime 3524 items: typing.List[DatasetItemClient] 3525 runs: typing.List[str] = [] # deprecated 3526 3527 def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]): 3528 """Initialize the DatasetClient.""" 3529 self.id = dataset.id 3530 self.name = dataset.name 3531 self.description = dataset.description 3532 self.project_id = dataset.project_id 3533 self.metadata = dataset.metadata 3534 self.dataset_name = dataset.name # for backward compatibility, to be deprecated 3535 self.created_at = dataset.created_at 3536 self.updated_at = dataset.updated_at 3537 self.items = items
Class for managing datasets in Langfuse.
Attributes:
- id (str): Unique identifier of the dataset.
- name (str): Name of the dataset.
- description (Optional[str]): Description of the dataset.
- metadata (Optional[typing.Any]): Additional metadata of the dataset.
- project_id (str): Identifier of the project to which the dataset belongs.
- dataset_name (str): Name of the dataset.
- created_at (datetime): Timestamp of dataset creation.
- updated_at (datetime): Timestamp of the last update to the dataset.
- items (List[DatasetItemClient]): List of dataset items associated with the dataset.
- runs (List[str]): List of dataset runs associated with the dataset. Deprecated.
Example:
Print the input of each dataset item in a dataset.
from langfuse import Langfuse langfuse = Langfuse() dataset = langfuse.get_dataset("<dataset_name>") for item in dataset.items: print(item.input)
3527 def __init__(self, dataset: Dataset, items: typing.List[DatasetItemClient]): 3528 """Initialize the DatasetClient.""" 3529 self.id = dataset.id 3530 self.name = dataset.name 3531 self.description = dataset.description 3532 self.project_id = dataset.project_id 3533 self.metadata = dataset.metadata 3534 self.dataset_name = dataset.name # for backward compatibility, to be deprecated 3535 self.created_at = dataset.created_at 3536 self.updated_at = dataset.updated_at 3537 self.items = items
Initialize the DatasetClient.