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