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