diff --git a/litellm/integrations/focus/focus_logger.py b/litellm/integrations/focus/focus_logger.py index ade1cf861b..a1b75fbfff 100644 --- a/litellm/integrations/focus/focus_logger.py +++ b/litellm/integrations/focus/focus_logger.py @@ -137,11 +137,15 @@ class FocusLogger(CustomLogger): ) -> None: """Register the export cron/interval job with the provided scheduler.""" - focus_loggers: List[ - CustomLogger - ] = litellm.logging_callback_manager.get_custom_loggers_for_type( - callback_type=FocusLogger - ) + # Use exact type match to exclude subclasses like VantageLogger, + # which have their own dedicated scheduling method. + focus_loggers: List[CustomLogger] = [ + cb + for cb in litellm.logging_callback_manager.get_custom_loggers_for_type( + callback_type=FocusLogger + ) + if type(cb) is FocusLogger + ] if not focus_loggers: verbose_logger.debug( "No Focus export logger registered; skipping scheduler" diff --git a/litellm/integrations/focus/serializers/csv.py b/litellm/integrations/focus/serializers/csv.py index 320c99517d..8e33c557be 100644 --- a/litellm/integrations/focus/serializers/csv.py +++ b/litellm/integrations/focus/serializers/csv.py @@ -16,6 +16,18 @@ class FocusCsvSerializer(FocusSerializer): def serialize(self, frame: pl.DataFrame) -> bytes: """Encode the provided frame as a CSV payload.""" + # Cast Decimal columns to Float64 so CSV output uses standard + # floating-point notation (e.g. "1.5") instead of fixed-point + # strings (e.g. "1.500000") that some parsers may reject. + decimal_cols = [ + col + for col, dtype in zip(frame.columns, frame.dtypes) + if isinstance(dtype, pl.Decimal) + ] + if decimal_cols: + frame = frame.with_columns( + [pl.col(c).cast(pl.Float64) for c in decimal_cols] + ) buffer = io.BytesIO() frame.write_csv(buffer) return buffer.getvalue()