33import copy
44import os
55import traceback
6- from collections .abc import MutableMapping , MutableSequence , MutableSet
76from typing import TYPE_CHECKING , Any , Dict , List , Optional , cast
87
98from packaging .version import Version
10- from pydantic import BaseModel
119
1210import litellm
1311from litellm ._logging import verbose_logger
@@ -71,8 +69,9 @@ def __init__(
7169 "flush_interval" : self .langfuse_flush_interval , # flush interval in seconds
7270 "httpx_client" : self .langfuse_client ,
7371 }
72+ self .langfuse_sdk_version : str = langfuse .version .__version__
7473
75- if Version (langfuse . version . __version__ ) >= Version ("2.6.0" ):
74+ if Version (self . langfuse_sdk_version ) >= Version ("2.6.0" ):
7675 parameters ["sdk_integration" ] = "litellm"
7776
7877 self .Langfuse = Langfuse (** parameters )
@@ -360,73 +359,6 @@ def _log_langfuse_v1(
360359 )
361360 )
362361
363- def is_base_type (self , value : Any ) -> bool :
364- # Check if the value is of a base type
365- base_types = (int , float , str , bool , list , dict , tuple )
366- return isinstance (value , base_types )
367-
368- def _prepare_metadata (self , metadata : Optional [dict ]) -> Any :
369- try :
370- if metadata is None :
371- return None
372-
373- # Filter out function types from the metadata
374- sanitized_metadata = {k : v for k , v in metadata .items () if not callable (v )}
375-
376- return copy .deepcopy (sanitized_metadata )
377- except Exception as e :
378- verbose_logger .debug (f"Langfuse Layer Error - { e } , metadata: { metadata } " )
379-
380- new_metadata : Dict [str , Any ] = {}
381-
382- # if metadata is not a MutableMapping, return an empty dict since we can't call items() on it
383- if not isinstance (metadata , MutableMapping ):
384- verbose_logger .debug (
385- "Langfuse Layer Logging - metadata is not a MutableMapping, returning empty dict"
386- )
387- return new_metadata
388-
389- for key , value in metadata .items ():
390- try :
391- if isinstance (value , MutableMapping ):
392- new_metadata [key ] = self ._prepare_metadata (cast (dict , value ))
393- elif isinstance (value , MutableSequence ):
394- # For lists or other mutable sequences
395- new_metadata [key ] = list (
396- (
397- self ._prepare_metadata (cast (dict , v ))
398- if isinstance (v , MutableMapping )
399- else copy .deepcopy (v )
400- )
401- for v in value
402- )
403- elif isinstance (value , MutableSet ):
404- # For sets specifically, create a new set by passing an iterable
405- new_metadata [key ] = set (
406- (
407- self ._prepare_metadata (cast (dict , v ))
408- if isinstance (v , MutableMapping )
409- else copy .deepcopy (v )
410- )
411- for v in value
412- )
413- elif isinstance (value , BaseModel ):
414- new_metadata [key ] = value .model_dump ()
415- elif self .is_base_type (value ):
416- new_metadata [key ] = value
417- else :
418- verbose_logger .debug (
419- f"Langfuse Layer Error - Unsupported metadata type: { type (value )} for key: { key } "
420- )
421- continue
422-
423- except (TypeError , copy .Error ):
424- verbose_logger .debug (
425- f"Langfuse Layer Error - Couldn't copy metadata key: { key } , type of key: { type (key )} , type of value: { type (value )} - { traceback .format_exc ()} "
426- )
427-
428- return new_metadata
429-
430362 def _log_langfuse_v2 ( # noqa: PLR0915
431363 self ,
432364 user_id ,
@@ -443,27 +375,17 @@ def _log_langfuse_v2( # noqa: PLR0915
443375 print_verbose ,
444376 litellm_call_id ,
445377 ) -> tuple :
446- import langfuse
447-
448378 verbose_logger .debug ("Langfuse Layer Logging - logging to langfuse v2" )
449379
450380 try :
451- metadata = self ._prepare_metadata (metadata )
452-
453- langfuse_version = Version (langfuse .version .__version__ )
454-
455- supports_tags = langfuse_version >= Version ("2.6.3" )
456- supports_prompt = langfuse_version >= Version ("2.7.3" )
457- supports_costs = langfuse_version >= Version ("2.7.3" )
458- supports_completion_start_time = langfuse_version >= Version ("2.7.3" )
459-
381+ metadata = metadata or {}
460382 standard_logging_object : Optional [StandardLoggingPayload ] = cast (
461383 Optional [StandardLoggingPayload ],
462384 kwargs .get ("standard_logging_object" , None ),
463385 )
464386 tags = (
465387 self ._get_langfuse_tags (standard_logging_object = standard_logging_object )
466- if supports_tags
388+ if self . _supports_tags ()
467389 else []
468390 )
469391
@@ -624,7 +546,7 @@ def _log_langfuse_v2( # noqa: PLR0915
624546 if aws_region_name :
625547 clean_metadata ["aws_region_name" ] = aws_region_name
626548
627- if supports_tags :
549+ if self . _supports_tags () :
628550 if "cache_hit" in kwargs :
629551 if kwargs ["cache_hit" ] is None :
630552 kwargs ["cache_hit" ] = False
@@ -670,7 +592,7 @@ def _log_langfuse_v2( # noqa: PLR0915
670592 usage = {
671593 "prompt_tokens" : _usage_obj .prompt_tokens ,
672594 "completion_tokens" : _usage_obj .completion_tokens ,
673- "total_cost" : cost if supports_costs else None ,
595+ "total_cost" : cost if self . _supports_costs () else None ,
674596 }
675597 generation_name = clean_metadata .pop ("generation_name" , None )
676598 if generation_name is None :
@@ -713,7 +635,7 @@ def _log_langfuse_v2( # noqa: PLR0915
713635 if parent_observation_id is not None :
714636 generation_params ["parent_observation_id" ] = parent_observation_id
715637
716- if supports_prompt :
638+ if self . _supports_prompt () :
717639 generation_params = _add_prompt_to_generation_params (
718640 generation_params = generation_params ,
719641 clean_metadata = clean_metadata ,
@@ -723,7 +645,7 @@ def _log_langfuse_v2( # noqa: PLR0915
723645 if output is not None and isinstance (output , str ) and level == "ERROR" :
724646 generation_params ["status_message" ] = output
725647
726- if supports_completion_start_time :
648+ if self . _supports_completion_start_time () :
727649 generation_params ["completion_start_time" ] = kwargs .get (
728650 "completion_start_time" , None
729651 )
@@ -770,6 +692,22 @@ def add_default_langfuse_tags(self, tags, kwargs, metadata):
770692 tags .append (f"cache_key:{ _cache_key } " )
771693 return tags
772694
695+ def _supports_tags (self ):
696+ """Check if current langfuse version supports tags"""
697+ return Version (self .langfuse_sdk_version ) >= Version ("2.6.3" )
698+
699+ def _supports_prompt (self ):
700+ """Check if current langfuse version supports prompt"""
701+ return Version (self .langfuse_sdk_version ) >= Version ("2.7.3" )
702+
703+ def _supports_costs (self ):
704+ """Check if current langfuse version supports costs"""
705+ return Version (self .langfuse_sdk_version ) >= Version ("2.7.3" )
706+
707+ def _supports_completion_start_time (self ):
708+ """Check if current langfuse version supports completion start time"""
709+ return Version (self .langfuse_sdk_version ) >= Version ("2.7.3" )
710+
773711
774712def _add_prompt_to_generation_params (
775713 generation_params : dict ,
0 commit comments