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