forked from phoenix/litellm-mirror
(fix) Langfuse key based logging (#6372)
* langfuse use helper for get_langfuse_logging_config * fix get_langfuse_logger_for_request * fix import * fix get_langfuse_logger_for_request * test_get_langfuse_logger_for_request_with_dynamic_params * unit testing for test_get_langfuse_logger_for_request_with_no_dynamic_params * parameterized langfuse testing * fix langfuse test * fix langfuse logging * fix test_aaalangfuse_logging_metadata * fix langfuse log metadata test * fix langfuse logger * use create_langfuse_logger_from_credentials * fix test_get_langfuse_logger_for_request_with_no_dynamic_params * fix correct langfuse/ folder structure * use static methods for langfuse logger * add commment on langfuse handler * fix linting error * add unit testing for langfuse logging * fix linting * fix failure handler langfuse
This commit is contained in:
parent
b70147f63b
commit
72a91ea9dd
7 changed files with 426 additions and 210 deletions
|
@ -4,7 +4,7 @@ import copy
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Any, Dict, Optional
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||||
|
|
||||||
from packaging.version import Version
|
from packaging.version import Version
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
@ -16,6 +16,11 @@ from litellm.secret_managers.main import str_to_bool
|
||||||
from litellm.types.integrations.langfuse import *
|
from litellm.types.integrations.langfuse import *
|
||||||
from litellm.types.utils import StandardCallbackDynamicParams, StandardLoggingPayload
|
from litellm.types.utils import StandardCallbackDynamicParams, StandardLoggingPayload
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from litellm.litellm_core_utils.litellm_logging import DynamicLoggingCache
|
||||||
|
else:
|
||||||
|
DynamicLoggingCache = Any
|
||||||
|
|
||||||
|
|
||||||
class LangFuseLogger:
|
class LangFuseLogger:
|
||||||
# Class variables or attributes
|
# Class variables or attributes
|
||||||
|
@ -798,53 +803,3 @@ def log_requester_metadata(clean_metadata: dict):
|
||||||
returned_metadata.update({"requester_metadata": requester_metadata})
|
returned_metadata.update({"requester_metadata": requester_metadata})
|
||||||
|
|
||||||
return returned_metadata
|
return returned_metadata
|
||||||
|
|
||||||
|
|
||||||
def get_langfuse_logging_config(
|
|
||||||
standard_callback_dynamic_params: StandardCallbackDynamicParams,
|
|
||||||
globalLangfuseLogger: Optional[LangFuseLogger] = None,
|
|
||||||
) -> LangfuseLoggingConfig:
|
|
||||||
"""
|
|
||||||
This function is used to get the Langfuse logging config for the Langfuse Logger.
|
|
||||||
It checks if the dynamic parameters are provided in the standard_callback_dynamic_params and uses them to get the Langfuse logging config.
|
|
||||||
|
|
||||||
If no dynamic parameters are provided, it uses the default values.
|
|
||||||
"""
|
|
||||||
# only use dynamic params if langfuse credentials are passed dynamically
|
|
||||||
if _dynamic_langfuse_credentials_are_passed(standard_callback_dynamic_params):
|
|
||||||
return LangfuseLoggingConfig(
|
|
||||||
langfuse_secret=standard_callback_dynamic_params.get("langfuse_secret")
|
|
||||||
or standard_callback_dynamic_params.get("langfuse_secret_key"),
|
|
||||||
langfuse_public_key=standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_public_key"
|
|
||||||
),
|
|
||||||
langfuse_host=standard_callback_dynamic_params.get("langfuse_host"),
|
|
||||||
)
|
|
||||||
elif globalLangfuseLogger is not None:
|
|
||||||
return LangfuseLoggingConfig(
|
|
||||||
langfuse_secret=globalLangfuseLogger.secret_key,
|
|
||||||
langfuse_public_key=globalLangfuseLogger.public_key,
|
|
||||||
langfuse_host=globalLangfuseLogger.langfuse_host,
|
|
||||||
)
|
|
||||||
raise ValueError(
|
|
||||||
"`langfuse` set as a callback but globalLangfuseLogger is not provided and dynamic params are not passed"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _dynamic_langfuse_credentials_are_passed(
|
|
||||||
standard_callback_dynamic_params: StandardCallbackDynamicParams,
|
|
||||||
) -> bool:
|
|
||||||
"""
|
|
||||||
This function is used to check if the dynamic langfuse credentials are passed in standard_callback_dynamic_params
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True if the dynamic langfuse credentials are passed, False otherwise
|
|
||||||
"""
|
|
||||||
if (
|
|
||||||
standard_callback_dynamic_params.get("langfuse_host") is not None
|
|
||||||
or standard_callback_dynamic_params.get("langfuse_public_key") is not None
|
|
||||||
or standard_callback_dynamic_params.get("langfuse_secret") is not None
|
|
||||||
or standard_callback_dynamic_params.get("langfuse_secret_key") is not None
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
return False
|
|
168
litellm/integrations/langfuse/langfuse_handler.py
Normal file
168
litellm/integrations/langfuse/langfuse_handler.py
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
"""
|
||||||
|
This file contains the LangFuseHandler class
|
||||||
|
|
||||||
|
Used to get the LangFuseLogger for a given request
|
||||||
|
|
||||||
|
Handles Key/Team Based Langfuse Logging
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||||
|
|
||||||
|
from litellm.litellm_core_utils.litellm_logging import StandardCallbackDynamicParams
|
||||||
|
|
||||||
|
from .langfuse import LangFuseLogger, LangfuseLoggingConfig
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from litellm.litellm_core_utils.litellm_logging import DynamicLoggingCache
|
||||||
|
else:
|
||||||
|
DynamicLoggingCache = Any
|
||||||
|
|
||||||
|
|
||||||
|
class LangFuseHandler:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_langfuse_logger_for_request(
|
||||||
|
standard_callback_dynamic_params: StandardCallbackDynamicParams,
|
||||||
|
in_memory_dynamic_logger_cache: DynamicLoggingCache,
|
||||||
|
globalLangfuseLogger: Optional[LangFuseLogger] = None,
|
||||||
|
) -> LangFuseLogger:
|
||||||
|
"""
|
||||||
|
This function is used to get the LangFuseLogger for a given request
|
||||||
|
|
||||||
|
1. If dynamic credentials are passed
|
||||||
|
- check if a LangFuseLogger is cached for the dynamic credentials
|
||||||
|
- if cached LangFuseLogger is not found, create a new LangFuseLogger and cache it
|
||||||
|
|
||||||
|
2. If dynamic credentials are not passed return the globalLangfuseLogger
|
||||||
|
|
||||||
|
"""
|
||||||
|
temp_langfuse_logger: Optional[LangFuseLogger] = globalLangfuseLogger
|
||||||
|
if (
|
||||||
|
LangFuseHandler._dynamic_langfuse_credentials_are_passed(
|
||||||
|
standard_callback_dynamic_params
|
||||||
|
)
|
||||||
|
is False
|
||||||
|
):
|
||||||
|
return LangFuseHandler._return_global_langfuse_logger(
|
||||||
|
globalLangfuseLogger=globalLangfuseLogger,
|
||||||
|
in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache,
|
||||||
|
)
|
||||||
|
|
||||||
|
# get langfuse logging config to use for this request, based on standard_callback_dynamic_params
|
||||||
|
_credentials = LangFuseHandler.get_dynamic_langfuse_logging_config(
|
||||||
|
globalLangfuseLogger=globalLangfuseLogger,
|
||||||
|
standard_callback_dynamic_params=standard_callback_dynamic_params,
|
||||||
|
)
|
||||||
|
credentials_dict = dict(_credentials)
|
||||||
|
|
||||||
|
# check if langfuse logger is already cached
|
||||||
|
temp_langfuse_logger = in_memory_dynamic_logger_cache.get_cache(
|
||||||
|
credentials=credentials_dict, service_name="langfuse"
|
||||||
|
)
|
||||||
|
|
||||||
|
# if not cached, create a new langfuse logger and cache it
|
||||||
|
if temp_langfuse_logger is None:
|
||||||
|
temp_langfuse_logger = (
|
||||||
|
LangFuseHandler._create_langfuse_logger_from_credentials(
|
||||||
|
credentials=credentials_dict,
|
||||||
|
in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return temp_langfuse_logger
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _return_global_langfuse_logger(
|
||||||
|
globalLangfuseLogger: Optional[LangFuseLogger],
|
||||||
|
in_memory_dynamic_logger_cache: DynamicLoggingCache,
|
||||||
|
) -> LangFuseLogger:
|
||||||
|
"""
|
||||||
|
Returns the Global LangfuseLogger set on litellm
|
||||||
|
|
||||||
|
(this is the default langfuse logger - used when no dynamic credentials are passed)
|
||||||
|
|
||||||
|
If no Global LangfuseLogger is set, it will check in_memory_dynamic_logger_cache for a cached LangFuseLogger
|
||||||
|
This function is used to return the globalLangfuseLogger if it exists, otherwise it will check in_memory_dynamic_logger_cache for a cached LangFuseLogger
|
||||||
|
"""
|
||||||
|
if globalLangfuseLogger is not None:
|
||||||
|
return globalLangfuseLogger
|
||||||
|
|
||||||
|
credentials_dict: Dict[str, Any] = (
|
||||||
|
{}
|
||||||
|
) # the global langfuse logger uses Environment Variables, there are no dynamic credentials
|
||||||
|
globalLangfuseLogger = in_memory_dynamic_logger_cache.get_cache(
|
||||||
|
credentials=credentials_dict,
|
||||||
|
service_name="langfuse",
|
||||||
|
)
|
||||||
|
if globalLangfuseLogger is None:
|
||||||
|
globalLangfuseLogger = (
|
||||||
|
LangFuseHandler._create_langfuse_logger_from_credentials(
|
||||||
|
credentials=credentials_dict,
|
||||||
|
in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return globalLangfuseLogger
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_langfuse_logger_from_credentials(
|
||||||
|
credentials: Dict,
|
||||||
|
in_memory_dynamic_logger_cache: DynamicLoggingCache,
|
||||||
|
) -> LangFuseLogger:
|
||||||
|
"""
|
||||||
|
This function is used to
|
||||||
|
1. create a LangFuseLogger from the credentials
|
||||||
|
2. cache the LangFuseLogger to prevent re-creating it for the same credentials
|
||||||
|
"""
|
||||||
|
|
||||||
|
langfuse_logger = LangFuseLogger(
|
||||||
|
langfuse_public_key=credentials.get("langfuse_public_key"),
|
||||||
|
langfuse_secret=credentials.get("langfuse_secret"),
|
||||||
|
langfuse_host=credentials.get("langfuse_host"),
|
||||||
|
)
|
||||||
|
in_memory_dynamic_logger_cache.set_cache(
|
||||||
|
credentials=credentials,
|
||||||
|
service_name="langfuse",
|
||||||
|
logging_obj=langfuse_logger,
|
||||||
|
)
|
||||||
|
return langfuse_logger
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_dynamic_langfuse_logging_config(
|
||||||
|
standard_callback_dynamic_params: StandardCallbackDynamicParams,
|
||||||
|
globalLangfuseLogger: Optional[LangFuseLogger] = None,
|
||||||
|
) -> LangfuseLoggingConfig:
|
||||||
|
"""
|
||||||
|
This function is used to get the Langfuse logging config to use for a given request.
|
||||||
|
|
||||||
|
It checks if the dynamic parameters are provided in the standard_callback_dynamic_params and uses them to get the Langfuse logging config.
|
||||||
|
|
||||||
|
If no dynamic parameters are provided, it uses the `globalLangfuseLogger` values
|
||||||
|
"""
|
||||||
|
# only use dynamic params if langfuse credentials are passed dynamically
|
||||||
|
return LangfuseLoggingConfig(
|
||||||
|
langfuse_secret=standard_callback_dynamic_params.get("langfuse_secret")
|
||||||
|
or standard_callback_dynamic_params.get("langfuse_secret_key"),
|
||||||
|
langfuse_public_key=standard_callback_dynamic_params.get(
|
||||||
|
"langfuse_public_key"
|
||||||
|
),
|
||||||
|
langfuse_host=standard_callback_dynamic_params.get("langfuse_host"),
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _dynamic_langfuse_credentials_are_passed(
|
||||||
|
standard_callback_dynamic_params: StandardCallbackDynamicParams,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
This function is used to check if the dynamic langfuse credentials are passed in standard_callback_dynamic_params
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the dynamic langfuse credentials are passed, False otherwise
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
standard_callback_dynamic_params.get("langfuse_host") is not None
|
||||||
|
or standard_callback_dynamic_params.get("langfuse_public_key") is not None
|
||||||
|
or standard_callback_dynamic_params.get("langfuse_secret") is not None
|
||||||
|
or standard_callback_dynamic_params.get("langfuse_secret_key") is not None
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
return False
|
|
@ -70,7 +70,8 @@ from ..integrations.gcs_bucket.gcs_bucket import GCSBucketLogger
|
||||||
from ..integrations.greenscale import GreenscaleLogger
|
from ..integrations.greenscale import GreenscaleLogger
|
||||||
from ..integrations.helicone import HeliconeLogger
|
from ..integrations.helicone import HeliconeLogger
|
||||||
from ..integrations.lago import LagoLogger
|
from ..integrations.lago import LagoLogger
|
||||||
from ..integrations.langfuse import LangFuseLogger
|
from ..integrations.langfuse.langfuse import LangFuseLogger
|
||||||
|
from ..integrations.langfuse.langfuse_handler import LangFuseHandler
|
||||||
from ..integrations.langsmith import LangsmithLogger
|
from ..integrations.langsmith import LangsmithLogger
|
||||||
from ..integrations.litedebugger import LiteDebugger
|
from ..integrations.litedebugger import LiteDebugger
|
||||||
from ..integrations.literal_ai import LiteralAILogger
|
from ..integrations.literal_ai import LiteralAILogger
|
||||||
|
@ -1116,74 +1117,13 @@ class Logging:
|
||||||
print_verbose("reaches langfuse for streaming logging!")
|
print_verbose("reaches langfuse for streaming logging!")
|
||||||
result = kwargs["complete_streaming_response"]
|
result = kwargs["complete_streaming_response"]
|
||||||
|
|
||||||
temp_langfuse_logger = langFuseLogger
|
langfuse_logger_to_use = LangFuseHandler.get_langfuse_logger_for_request(
|
||||||
if langFuseLogger is None or (
|
globalLangfuseLogger=langFuseLogger,
|
||||||
(
|
standard_callback_dynamic_params=self.standard_callback_dynamic_params,
|
||||||
self.standard_callback_dynamic_params.get(
|
in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache,
|
||||||
"langfuse_public_key"
|
|
||||||
)
|
)
|
||||||
is not None
|
if langfuse_logger_to_use is not None:
|
||||||
and self.standard_callback_dynamic_params.get(
|
_response = langfuse_logger_to_use.log_event(
|
||||||
"langfuse_public_key"
|
|
||||||
)
|
|
||||||
!= langFuseLogger.public_key
|
|
||||||
)
|
|
||||||
or (
|
|
||||||
self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_secret"
|
|
||||||
)
|
|
||||||
is not None
|
|
||||||
and self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_secret"
|
|
||||||
)
|
|
||||||
!= langFuseLogger.secret_key
|
|
||||||
)
|
|
||||||
or (
|
|
||||||
self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_host"
|
|
||||||
)
|
|
||||||
is not None
|
|
||||||
and self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_host"
|
|
||||||
)
|
|
||||||
!= langFuseLogger.langfuse_host
|
|
||||||
)
|
|
||||||
):
|
|
||||||
credentials = {
|
|
||||||
"langfuse_public_key": self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_public_key"
|
|
||||||
),
|
|
||||||
"langfuse_secret": self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_secret"
|
|
||||||
),
|
|
||||||
"langfuse_host": self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_host"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
temp_langfuse_logger = (
|
|
||||||
in_memory_dynamic_logger_cache.get_cache(
|
|
||||||
credentials=credentials, service_name="langfuse"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if temp_langfuse_logger is None:
|
|
||||||
temp_langfuse_logger = LangFuseLogger(
|
|
||||||
langfuse_public_key=self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_public_key"
|
|
||||||
),
|
|
||||||
langfuse_secret=self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_secret"
|
|
||||||
),
|
|
||||||
langfuse_host=self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_host"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
in_memory_dynamic_logger_cache.set_cache(
|
|
||||||
credentials=credentials,
|
|
||||||
service_name="langfuse",
|
|
||||||
logging_obj=temp_langfuse_logger,
|
|
||||||
)
|
|
||||||
if temp_langfuse_logger is not None:
|
|
||||||
_response = temp_langfuse_logger.log_event(
|
|
||||||
kwargs=kwargs,
|
kwargs=kwargs,
|
||||||
response_obj=result,
|
response_obj=result,
|
||||||
start_time=start_time,
|
start_time=start_time,
|
||||||
|
@ -1909,50 +1849,12 @@ class Logging:
|
||||||
): # copy.deepcopy raises errors as this could be a coroutine
|
): # copy.deepcopy raises errors as this could be a coroutine
|
||||||
kwargs[k] = v
|
kwargs[k] = v
|
||||||
# this only logs streaming once, complete_streaming_response exists i.e when stream ends
|
# this only logs streaming once, complete_streaming_response exists i.e when stream ends
|
||||||
if langFuseLogger is None or (
|
langfuse_logger_to_use = LangFuseHandler.get_langfuse_logger_for_request(
|
||||||
(
|
globalLangfuseLogger=langFuseLogger,
|
||||||
self.standard_callback_dynamic_params.get(
|
standard_callback_dynamic_params=self.standard_callback_dynamic_params,
|
||||||
"langfuse_public_key"
|
in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache,
|
||||||
)
|
)
|
||||||
is not None
|
_response = langfuse_logger_to_use.log_event(
|
||||||
and self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_public_key"
|
|
||||||
)
|
|
||||||
!= langFuseLogger.public_key
|
|
||||||
)
|
|
||||||
or (
|
|
||||||
self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_public_key"
|
|
||||||
)
|
|
||||||
is not None
|
|
||||||
and self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_public_key"
|
|
||||||
)
|
|
||||||
!= langFuseLogger.public_key
|
|
||||||
)
|
|
||||||
or (
|
|
||||||
self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_host"
|
|
||||||
)
|
|
||||||
is not None
|
|
||||||
and self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_host"
|
|
||||||
)
|
|
||||||
!= langFuseLogger.langfuse_host
|
|
||||||
)
|
|
||||||
):
|
|
||||||
langFuseLogger = LangFuseLogger(
|
|
||||||
langfuse_public_key=self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_public_key"
|
|
||||||
),
|
|
||||||
langfuse_secret=self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_secret"
|
|
||||||
),
|
|
||||||
langfuse_host=self.standard_callback_dynamic_params.get(
|
|
||||||
"langfuse_host"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
_response = langFuseLogger.log_event(
|
|
||||||
start_time=start_time,
|
start_time=start_time,
|
||||||
end_time=end_time,
|
end_time=end_time,
|
||||||
response_obj=None,
|
response_obj=None,
|
||||||
|
|
|
@ -120,7 +120,7 @@ async def health_services_endpoint( # noqa: PLR0915
|
||||||
}
|
}
|
||||||
|
|
||||||
if service == "langfuse":
|
if service == "langfuse":
|
||||||
from litellm.integrations.langfuse import LangFuseLogger
|
from litellm.integrations.langfuse.langfuse import LangFuseLogger
|
||||||
|
|
||||||
langfuse_logger = LangFuseLogger()
|
langfuse_logger = LangFuseLogger()
|
||||||
langfuse_logger.Langfuse.auth_check()
|
langfuse_logger.Langfuse.auth_check()
|
||||||
|
|
|
@ -1,48 +1,20 @@
|
||||||
model_list:
|
model_list:
|
||||||
################################################################################
|
|
||||||
# Azure
|
|
||||||
- model_name: gpt-4o-mini
|
|
||||||
litellm_params:
|
|
||||||
model: azure/gpt-4o-mini
|
|
||||||
api_base: https://amazin-prod.openai.azure.com
|
|
||||||
api_key: "os.environ/AZURE_GPT_4O"
|
|
||||||
deployment_id: gpt-4o-mini
|
|
||||||
- model_name: gpt-4o
|
- model_name: gpt-4o
|
||||||
litellm_params:
|
litellm_params:
|
||||||
model: azure/gpt-4o
|
model: gpt-4o
|
||||||
api_base: https://very-cool-prod.openai.azure.com
|
api_key: os.environ/OPENAI_API_KEY
|
||||||
api_key: "os.environ/AZURE_GPT_4O"
|
tpm: 1000000
|
||||||
deployment_id: gpt-4o
|
rpm: 10000
|
||||||
|
|
||||||
################################################################################
|
|
||||||
# Fireworks
|
|
||||||
- model_name: fireworks-llama-v3p1-405b-instruct
|
|
||||||
litellm_params:
|
|
||||||
model: fireworks_ai/accounts/fireworks/models/llama-v3p1-405b-instruct
|
|
||||||
api_key: "os.environ/FIREWORKS"
|
|
||||||
- model_name: fireworks-llama-v3p1-70b-instruct
|
|
||||||
litellm_params:
|
|
||||||
model: fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct
|
|
||||||
api_key: "os.environ/FIREWORKS"
|
|
||||||
|
|
||||||
general_settings:
|
general_settings:
|
||||||
alerting_threshold: 300 # sends alerts if requests hang for 5min+ and responses take 5min+
|
# master key is set via env var
|
||||||
litellm_settings: # module level litellm settings - https://github.com/BerriAI/litellm/blob/main/litellm/__init__.py
|
# master_key: #######
|
||||||
success_callback: ["prometheus"]
|
proxy_batch_write_at: 60 # Batch write spend updates every 60s
|
||||||
service_callback: ["prometheus_system"]
|
|
||||||
drop_params: False # Raise an exception if the openai param being passed in isn't supported.
|
|
||||||
cache: false
|
|
||||||
default_internal_user_params:
|
|
||||||
user_role: os.environ/DEFAULT_USER_ROLE
|
|
||||||
|
|
||||||
success_callback: ["s3"]
|
litellm_settings:
|
||||||
s3_callback_params:
|
success_callback: ["langfuse"]
|
||||||
s3_bucket_name: logs-bucket-litellm # AWS Bucket Name for S3
|
|
||||||
s3_region_name: us-west-2 # AWS Region Name for S3
|
|
||||||
s3_aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID # us os.environ/<variable name> to pass environment variables. This is AWS Access Key ID for S3
|
|
||||||
s3_aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY # AWS Secret Access Key for S3
|
|
||||||
s3_path: my-test-path # [OPTIONAL] set path in bucket you want to write logs to
|
|
||||||
s3_endpoint_url: https://s3.amazonaws.com # [OPTIONAL] S3 endpoint URL, if you want to use Backblaze/cloudflare s3 buckets
|
|
||||||
|
|
||||||
router_settings:
|
# https://docs.litellm.ai/docs/proxy/reliability#default-fallbacks
|
||||||
routing_strategy: simple-shuffle # "simple-shuffle" shown to result in highest throughput. https://docs.litellm.ai/docs/proxy/configs#load-balancing
|
default_fallbacks: ["gpt-4o-2024-08-06", "claude-3-5-sonnet-20240620"]
|
||||||
|
fallbacks: [{"gpt-4o-2024-08-06": ["claude-3-5-sonnet-20240620"]}, {"gpt-4o-2024-05-13": ["claude-3-5-sonnet-20240620"]}]
|
|
@ -428,7 +428,7 @@ async def test_aaalangfuse_logging_metadata(langfuse_client):
|
||||||
|
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
langfuse_client.flush()
|
langfuse_client.flush()
|
||||||
# await asyncio.sleep(10)
|
await asyncio.sleep(4)
|
||||||
|
|
||||||
# Tests the metadata filtering and the override of the output to be the last generation
|
# Tests the metadata filtering and the override of the output to be the last generation
|
||||||
for trace_id, generation_ids in trace_identifiers.items():
|
for trace_id, generation_ids in trace_identifiers.items():
|
||||||
|
@ -625,7 +625,7 @@ def test_aaalangfuse_existing_trace_id():
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import litellm
|
import litellm
|
||||||
from litellm.integrations.langfuse import LangFuseLogger
|
from litellm.integrations.langfuse.langfuse import LangFuseLogger
|
||||||
|
|
||||||
langfuse_Logger = LangFuseLogger(
|
langfuse_Logger = LangFuseLogger(
|
||||||
langfuse_public_key=os.getenv("LANGFUSE_PROJECT2_PUBLIC"),
|
langfuse_public_key=os.getenv("LANGFUSE_PROJECT2_PUBLIC"),
|
||||||
|
@ -1125,7 +1125,7 @@ generation_params = {
|
||||||
)
|
)
|
||||||
def test_langfuse_prompt_type(prompt):
|
def test_langfuse_prompt_type(prompt):
|
||||||
|
|
||||||
from litellm.integrations.langfuse import _add_prompt_to_generation_params
|
from litellm.integrations.langfuse.langfuse import _add_prompt_to_generation_params
|
||||||
|
|
||||||
clean_metadata = {
|
clean_metadata = {
|
||||||
"prompt": {
|
"prompt": {
|
||||||
|
@ -1232,7 +1232,7 @@ def test_langfuse_prompt_type(prompt):
|
||||||
|
|
||||||
|
|
||||||
def test_langfuse_logging_metadata():
|
def test_langfuse_logging_metadata():
|
||||||
from litellm.integrations.langfuse import log_requester_metadata
|
from litellm.integrations.langfuse.langfuse import log_requester_metadata
|
||||||
|
|
||||||
metadata = {"key": "value", "requester_metadata": {"key": "value"}}
|
metadata = {"key": "value", "requester_metadata": {"key": "value"}}
|
||||||
|
|
||||||
|
|
219
tests/logging_callback_tests/test_langfuse_unit_tests.py
Normal file
219
tests/logging_callback_tests/test_langfuse_unit_tests.py
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from pydantic.main import Model
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0, os.path.abspath("../..")
|
||||||
|
) # Adds the parent directory to the system-path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import litellm
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from litellm._logging import verbose_logger
|
||||||
|
from litellm.integrations.langfuse.langfuse import (
|
||||||
|
LangFuseLogger,
|
||||||
|
)
|
||||||
|
from litellm.integrations.langfuse.langfuse_handler import LangFuseHandler
|
||||||
|
from litellm.types.utils import StandardCallbackDynamicParams
|
||||||
|
from litellm.litellm_core_utils.litellm_logging import DynamicLoggingCache
|
||||||
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dynamic_logging_cache():
|
||||||
|
return DynamicLoggingCache()
|
||||||
|
|
||||||
|
|
||||||
|
global_langfuse_logger = LangFuseLogger(
|
||||||
|
langfuse_public_key="global_public_key",
|
||||||
|
langfuse_secret="global_secret",
|
||||||
|
langfuse_host="https://global.langfuse.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# IMPORTANT: Test that passing both langfuse_secret_key and langfuse_secret works
|
||||||
|
standard_params_1 = StandardCallbackDynamicParams(
|
||||||
|
langfuse_public_key="test_public_key",
|
||||||
|
langfuse_secret="test_secret",
|
||||||
|
langfuse_host="https://test.langfuse.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
standard_params_2 = StandardCallbackDynamicParams(
|
||||||
|
langfuse_public_key="test_public_key",
|
||||||
|
langfuse_secret_key="test_secret",
|
||||||
|
langfuse_host="https://test.langfuse.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("globalLangfuseLogger", [None, global_langfuse_logger])
|
||||||
|
@pytest.mark.parametrize("standard_params", [standard_params_1, standard_params_2])
|
||||||
|
def test_get_langfuse_logger_for_request_with_dynamic_params(
|
||||||
|
dynamic_logging_cache, globalLangfuseLogger, standard_params
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
If StandardCallbackDynamicParams contain langfuse credentials the returned Langfuse logger should use the dynamic params
|
||||||
|
|
||||||
|
the new Langfuse logger should be cached
|
||||||
|
|
||||||
|
Even if globalLangfuseLogger is provided, it should use dynamic params if they are passed
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = LangFuseHandler.get_langfuse_logger_for_request(
|
||||||
|
standard_callback_dynamic_params=standard_params,
|
||||||
|
in_memory_dynamic_logger_cache=dynamic_logging_cache,
|
||||||
|
globalLangfuseLogger=globalLangfuseLogger,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert isinstance(result, LangFuseLogger)
|
||||||
|
assert result.public_key == "test_public_key"
|
||||||
|
assert result.secret_key == "test_secret"
|
||||||
|
assert result.langfuse_host == "https://test.langfuse.com"
|
||||||
|
|
||||||
|
print("langfuse logger=", result)
|
||||||
|
print("vars in langfuse logger=", vars(result))
|
||||||
|
|
||||||
|
# Check if the logger is cached
|
||||||
|
cached_logger = dynamic_logging_cache.get_cache(
|
||||||
|
credentials={
|
||||||
|
"langfuse_public_key": "test_public_key",
|
||||||
|
"langfuse_secret": "test_secret",
|
||||||
|
"langfuse_host": "https://test.langfuse.com",
|
||||||
|
},
|
||||||
|
service_name="langfuse",
|
||||||
|
)
|
||||||
|
assert cached_logger is result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("globalLangfuseLogger", [None, global_langfuse_logger])
|
||||||
|
def test_get_langfuse_logger_for_request_with_no_dynamic_params(
|
||||||
|
dynamic_logging_cache, globalLangfuseLogger
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
If StandardCallbackDynamicParams are not provided, the globalLangfuseLogger should be returned
|
||||||
|
"""
|
||||||
|
result = LangFuseHandler.get_langfuse_logger_for_request(
|
||||||
|
standard_callback_dynamic_params=StandardCallbackDynamicParams(),
|
||||||
|
in_memory_dynamic_logger_cache=dynamic_logging_cache,
|
||||||
|
globalLangfuseLogger=globalLangfuseLogger,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result is not None
|
||||||
|
assert isinstance(result, LangFuseLogger)
|
||||||
|
|
||||||
|
print("langfuse logger=", result)
|
||||||
|
|
||||||
|
if globalLangfuseLogger is not None:
|
||||||
|
assert result.public_key == "global_public_key"
|
||||||
|
assert result.secret_key == "global_secret"
|
||||||
|
assert result.langfuse_host == "https://global.langfuse.com"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dynamic_langfuse_credentials_are_passed():
|
||||||
|
# Test when credentials are passed
|
||||||
|
params_with_credentials = StandardCallbackDynamicParams(
|
||||||
|
langfuse_public_key="test_key",
|
||||||
|
langfuse_secret="test_secret",
|
||||||
|
langfuse_host="https://test.langfuse.com",
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
LangFuseHandler._dynamic_langfuse_credentials_are_passed(
|
||||||
|
params_with_credentials
|
||||||
|
)
|
||||||
|
is True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test when no credentials are passed
|
||||||
|
params_without_credentials = StandardCallbackDynamicParams()
|
||||||
|
assert (
|
||||||
|
LangFuseHandler._dynamic_langfuse_credentials_are_passed(
|
||||||
|
params_without_credentials
|
||||||
|
)
|
||||||
|
is False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test when only some credentials are passed
|
||||||
|
params_partial_credentials = StandardCallbackDynamicParams(
|
||||||
|
langfuse_public_key="test_key"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
LangFuseHandler._dynamic_langfuse_credentials_are_passed(
|
||||||
|
params_partial_credentials
|
||||||
|
)
|
||||||
|
is True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_dynamic_langfuse_logging_config():
|
||||||
|
# Test with dynamic params
|
||||||
|
dynamic_params = StandardCallbackDynamicParams(
|
||||||
|
langfuse_public_key="dynamic_key",
|
||||||
|
langfuse_secret="dynamic_secret",
|
||||||
|
langfuse_host="https://dynamic.langfuse.com",
|
||||||
|
)
|
||||||
|
config = LangFuseHandler.get_dynamic_langfuse_logging_config(dynamic_params)
|
||||||
|
assert config["langfuse_public_key"] == "dynamic_key"
|
||||||
|
assert config["langfuse_secret"] == "dynamic_secret"
|
||||||
|
assert config["langfuse_host"] == "https://dynamic.langfuse.com"
|
||||||
|
|
||||||
|
# Test with no dynamic params
|
||||||
|
empty_params = StandardCallbackDynamicParams()
|
||||||
|
config = LangFuseHandler.get_dynamic_langfuse_logging_config(empty_params)
|
||||||
|
assert config["langfuse_public_key"] is None
|
||||||
|
assert config["langfuse_secret"] is None
|
||||||
|
assert config["langfuse_host"] is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_return_global_langfuse_logger():
|
||||||
|
mock_cache = Mock()
|
||||||
|
global_logger = LangFuseLogger(
|
||||||
|
langfuse_public_key="global_key", langfuse_secret="global_secret"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with existing global logger
|
||||||
|
result = LangFuseHandler._return_global_langfuse_logger(global_logger, mock_cache)
|
||||||
|
assert result == global_logger
|
||||||
|
|
||||||
|
# Test without global logger, but with cached logger, should return cached logger
|
||||||
|
mock_cache.get_cache.return_value = global_logger
|
||||||
|
result = LangFuseHandler._return_global_langfuse_logger(None, mock_cache)
|
||||||
|
assert result == global_logger
|
||||||
|
|
||||||
|
# Test without global logger and without cached logger, should create new logger
|
||||||
|
mock_cache.get_cache.return_value = None
|
||||||
|
with patch.object(
|
||||||
|
LangFuseHandler,
|
||||||
|
"_create_langfuse_logger_from_credentials",
|
||||||
|
return_value=global_logger,
|
||||||
|
):
|
||||||
|
result = LangFuseHandler._return_global_langfuse_logger(None, mock_cache)
|
||||||
|
assert result == global_logger
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_langfuse_logger_for_request_with_cached_logger():
|
||||||
|
"""
|
||||||
|
Test that get_langfuse_logger_for_request returns the cached logger if it exists when dynamic params are passed
|
||||||
|
"""
|
||||||
|
mock_cache = Mock()
|
||||||
|
cached_logger = LangFuseLogger(
|
||||||
|
langfuse_public_key="cached_key", langfuse_secret="cached_secret"
|
||||||
|
)
|
||||||
|
mock_cache.get_cache.return_value = cached_logger
|
||||||
|
|
||||||
|
dynamic_params = StandardCallbackDynamicParams(
|
||||||
|
langfuse_public_key="test_key",
|
||||||
|
langfuse_secret="test_secret",
|
||||||
|
langfuse_host="https://test.langfuse.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
result = LangFuseHandler.get_langfuse_logger_for_request(
|
||||||
|
standard_callback_dynamic_params=dynamic_params,
|
||||||
|
in_memory_dynamic_logger_cache=mock_cache,
|
||||||
|
globalLangfuseLogger=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result == cached_logger
|
||||||
|
mock_cache.get_cache.assert_called_once()
|
Loading…
Add table
Add a link
Reference in a new issue