mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
Merge pull request #25807 from BerriAI/litellm_fix_provider_headers_in_logging
fix(logging): preserve provider response headers in StandardLoggingPayload
This commit is contained in:
commit
7a6b7ade03
4 changed files with 280 additions and 228 deletions
|
|
@ -354,9 +354,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
)
|
||||
self.function_id = function_id
|
||||
self.streaming_chunks: List[Any] = [] # for generating complete stream response
|
||||
self.sync_streaming_chunks: List[
|
||||
Any
|
||||
] = [] # for generating complete stream response
|
||||
self.sync_streaming_chunks: List[Any] = (
|
||||
[]
|
||||
) # for generating complete stream response
|
||||
self.log_raw_request_response = log_raw_request_response
|
||||
|
||||
# Initialize dynamic callbacks
|
||||
|
|
@ -811,9 +811,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
prompt_spec=prompt_spec,
|
||||
dynamic_callback_params=dynamic_callback_params,
|
||||
):
|
||||
self.model_call_details[
|
||||
"prompt_integration"
|
||||
] = logger.__class__.__name__
|
||||
self.model_call_details["prompt_integration"] = (
|
||||
logger.__class__.__name__
|
||||
)
|
||||
return logger
|
||||
except Exception:
|
||||
# If check fails, continue to next logger
|
||||
|
|
@ -881,9 +881,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
if anthropic_cache_control_logger := AnthropicCacheControlHook.get_custom_logger_for_anthropic_cache_control_hook(
|
||||
non_default_params
|
||||
):
|
||||
self.model_call_details[
|
||||
"prompt_integration"
|
||||
] = anthropic_cache_control_logger.__class__.__name__
|
||||
self.model_call_details["prompt_integration"] = (
|
||||
anthropic_cache_control_logger.__class__.__name__
|
||||
)
|
||||
return anthropic_cache_control_logger
|
||||
|
||||
#########################################################
|
||||
|
|
@ -895,9 +895,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
internal_usage_cache=None,
|
||||
llm_router=None,
|
||||
)
|
||||
self.model_call_details[
|
||||
"prompt_integration"
|
||||
] = vector_store_custom_logger.__class__.__name__
|
||||
self.model_call_details["prompt_integration"] = (
|
||||
vector_store_custom_logger.__class__.__name__
|
||||
)
|
||||
# Add to global callbacks so post-call hooks are invoked
|
||||
if (
|
||||
vector_store_custom_logger
|
||||
|
|
@ -957,9 +957,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
model
|
||||
): # if model name was changes pre-call, overwrite the initial model call name with the new one
|
||||
self.model_call_details["model"] = model
|
||||
self.model_call_details["litellm_params"][
|
||||
"api_base"
|
||||
] = self._get_masked_api_base(additional_args.get("api_base", ""))
|
||||
self.model_call_details["litellm_params"]["api_base"] = (
|
||||
self._get_masked_api_base(additional_args.get("api_base", ""))
|
||||
)
|
||||
|
||||
def pre_call(self, input, api_key, model=None, additional_args={}): # noqa: PLR0915
|
||||
# Log the exact input to the LLM API
|
||||
|
|
@ -988,10 +988,10 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
try:
|
||||
# [Non-blocking Extra Debug Information in metadata]
|
||||
if turn_off_message_logging is True:
|
||||
_metadata[
|
||||
"raw_request"
|
||||
] = "redacted by litellm. \
|
||||
_metadata["raw_request"] = (
|
||||
"redacted by litellm. \
|
||||
'litellm.turn_off_message_logging=True'"
|
||||
)
|
||||
else:
|
||||
curl_command = self._get_request_curl_command(
|
||||
api_base=additional_args.get("api_base", ""),
|
||||
|
|
@ -1002,34 +1002,34 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
|
||||
_metadata["raw_request"] = str(curl_command)
|
||||
# split up, so it's easier to parse in the UI
|
||||
self.model_call_details[
|
||||
"raw_request_typed_dict"
|
||||
] = RawRequestTypedDict(
|
||||
raw_request_api_base=str(
|
||||
additional_args.get("api_base") or ""
|
||||
),
|
||||
raw_request_body=self._get_raw_request_body(
|
||||
additional_args.get("complete_input_dict", {})
|
||||
),
|
||||
# NOTE: setting ignore_sensitive_headers to True will cause
|
||||
# the Authorization header to be leaked when calls to the health
|
||||
# endpoint are made and fail.
|
||||
raw_request_headers=self._get_masked_headers(
|
||||
additional_args.get("headers", {}) or {},
|
||||
),
|
||||
error=None,
|
||||
self.model_call_details["raw_request_typed_dict"] = (
|
||||
RawRequestTypedDict(
|
||||
raw_request_api_base=str(
|
||||
additional_args.get("api_base") or ""
|
||||
),
|
||||
raw_request_body=self._get_raw_request_body(
|
||||
additional_args.get("complete_input_dict", {})
|
||||
),
|
||||
# NOTE: setting ignore_sensitive_headers to True will cause
|
||||
# the Authorization header to be leaked when calls to the health
|
||||
# endpoint are made and fail.
|
||||
raw_request_headers=self._get_masked_headers(
|
||||
additional_args.get("headers", {}) or {},
|
||||
),
|
||||
error=None,
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
self.model_call_details[
|
||||
"raw_request_typed_dict"
|
||||
] = RawRequestTypedDict(
|
||||
error=str(e),
|
||||
self.model_call_details["raw_request_typed_dict"] = (
|
||||
RawRequestTypedDict(
|
||||
error=str(e),
|
||||
)
|
||||
)
|
||||
_metadata[
|
||||
"raw_request"
|
||||
] = "Unable to Log \
|
||||
_metadata["raw_request"] = (
|
||||
"Unable to Log \
|
||||
raw request: {}".format(
|
||||
str(e)
|
||||
str(e)
|
||||
)
|
||||
)
|
||||
if getattr(self, "logger_fn", None) and callable(self.logger_fn):
|
||||
try:
|
||||
|
|
@ -1330,13 +1330,13 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
for callback in callbacks:
|
||||
try:
|
||||
if isinstance(callback, CustomLogger):
|
||||
response: Optional[
|
||||
MCPPostCallResponseObject
|
||||
] = await callback.async_post_mcp_tool_call_hook(
|
||||
kwargs=kwargs,
|
||||
response_obj=post_mcp_tool_call_response_obj,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
response: Optional[MCPPostCallResponseObject] = (
|
||||
await callback.async_post_mcp_tool_call_hook(
|
||||
kwargs=kwargs,
|
||||
response_obj=post_mcp_tool_call_response_obj,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
)
|
||||
)
|
||||
######################################################################
|
||||
# if any of the callbacks modify the response, use the modified response
|
||||
|
|
@ -1543,9 +1543,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
verbose_logger.debug(
|
||||
f"response_cost_failure_debug_information: {debug_info}"
|
||||
)
|
||||
self.model_call_details[
|
||||
"response_cost_failure_debug_information"
|
||||
] = debug_info
|
||||
self.model_call_details["response_cost_failure_debug_information"] = (
|
||||
debug_info
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
|
|
@ -1571,9 +1571,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
verbose_logger.debug(
|
||||
f"response_cost_failure_debug_information: {debug_info}"
|
||||
)
|
||||
self.model_call_details[
|
||||
"response_cost_failure_debug_information"
|
||||
] = debug_info
|
||||
self.model_call_details["response_cost_failure_debug_information"] = (
|
||||
debug_info
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -1722,9 +1722,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
self.model_call_details["litellm_params"].setdefault("metadata", {})
|
||||
if self.model_call_details["litellm_params"]["metadata"] is None:
|
||||
self.model_call_details["litellm_params"]["metadata"] = {}
|
||||
self.model_call_details["litellm_params"]["metadata"][
|
||||
"hidden_params"
|
||||
] = getattr(logging_result, "_hidden_params", {})
|
||||
self.model_call_details["litellm_params"]["metadata"]["hidden_params"] = (
|
||||
getattr(logging_result, "_hidden_params", {})
|
||||
)
|
||||
|
||||
def _process_hidden_params_and_response_cost(
|
||||
self,
|
||||
|
|
@ -1753,9 +1753,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
result=logging_result
|
||||
)
|
||||
|
||||
self.model_call_details[
|
||||
"standard_logging_object"
|
||||
] = self._build_standard_logging_payload(logging_result, start_time, end_time)
|
||||
self.model_call_details["standard_logging_object"] = (
|
||||
self._build_standard_logging_payload(logging_result, start_time, end_time)
|
||||
)
|
||||
|
||||
if (
|
||||
standard_logging_payload := self.model_call_details.get(
|
||||
|
|
@ -1833,9 +1833,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
end_time = datetime.datetime.now()
|
||||
if self.completion_start_time is None:
|
||||
self.completion_start_time = end_time
|
||||
self.model_call_details[
|
||||
"completion_start_time"
|
||||
] = self.completion_start_time
|
||||
self.model_call_details["completion_start_time"] = (
|
||||
self.completion_start_time
|
||||
)
|
||||
|
||||
self.model_call_details["log_event_type"] = "successful_api_call"
|
||||
self.model_call_details["end_time"] = end_time
|
||||
|
|
@ -1872,10 +1872,10 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
end_time=end_time,
|
||||
)
|
||||
elif isinstance(result, dict) or isinstance(result, list):
|
||||
self.model_call_details[
|
||||
"standard_logging_object"
|
||||
] = self._build_standard_logging_payload(
|
||||
result, start_time, end_time
|
||||
self.model_call_details["standard_logging_object"] = (
|
||||
self._build_standard_logging_payload(
|
||||
result, start_time, end_time
|
||||
)
|
||||
)
|
||||
if (
|
||||
standard_logging_payload := self.model_call_details.get(
|
||||
|
|
@ -1884,9 +1884,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
) is not None:
|
||||
emit_standard_logging_payload(standard_logging_payload)
|
||||
elif standard_logging_object is not None:
|
||||
self.model_call_details[
|
||||
"standard_logging_object"
|
||||
] = standard_logging_object
|
||||
self.model_call_details["standard_logging_object"] = (
|
||||
standard_logging_object
|
||||
)
|
||||
else:
|
||||
self.model_call_details["response_cost"] = None
|
||||
|
||||
|
|
@ -2044,20 +2044,20 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
verbose_logger.debug(
|
||||
"Logging Details LiteLLM-Success Call streaming complete"
|
||||
)
|
||||
self.model_call_details[
|
||||
"complete_streaming_response"
|
||||
] = complete_streaming_response
|
||||
self.model_call_details[
|
||||
"response_cost"
|
||||
] = self._response_cost_calculator(result=complete_streaming_response)
|
||||
self.model_call_details["complete_streaming_response"] = (
|
||||
complete_streaming_response
|
||||
)
|
||||
self.model_call_details["response_cost"] = (
|
||||
self._response_cost_calculator(result=complete_streaming_response)
|
||||
)
|
||||
self._merge_hidden_params_from_response_into_metadata(
|
||||
complete_streaming_response
|
||||
)
|
||||
## STANDARDIZED LOGGING PAYLOAD
|
||||
self.model_call_details[
|
||||
"standard_logging_object"
|
||||
] = self._build_standard_logging_payload(
|
||||
complete_streaming_response, start_time, end_time
|
||||
self.model_call_details["standard_logging_object"] = (
|
||||
self._build_standard_logging_payload(
|
||||
complete_streaming_response, start_time, end_time
|
||||
)
|
||||
)
|
||||
if (
|
||||
standard_logging_payload := self.model_call_details.get(
|
||||
|
|
@ -2391,10 +2391,10 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
)
|
||||
else:
|
||||
if self.stream and complete_streaming_response:
|
||||
self.model_call_details[
|
||||
"complete_response"
|
||||
] = self.model_call_details.get(
|
||||
"complete_streaming_response", {}
|
||||
self.model_call_details["complete_response"] = (
|
||||
self.model_call_details.get(
|
||||
"complete_streaming_response", {}
|
||||
)
|
||||
)
|
||||
result = self.model_call_details["complete_response"]
|
||||
openMeterLogger.log_success_event(
|
||||
|
|
@ -2418,10 +2418,10 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
)
|
||||
else:
|
||||
if self.stream and complete_streaming_response:
|
||||
self.model_call_details[
|
||||
"complete_response"
|
||||
] = self.model_call_details.get(
|
||||
"complete_streaming_response", {}
|
||||
self.model_call_details["complete_response"] = (
|
||||
self.model_call_details.get(
|
||||
"complete_streaming_response", {}
|
||||
)
|
||||
)
|
||||
result = self.model_call_details["complete_response"]
|
||||
|
||||
|
|
@ -2560,9 +2560,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
if complete_streaming_response is not None:
|
||||
print_verbose("Async success callbacks: Got a complete streaming response")
|
||||
|
||||
self.model_call_details[
|
||||
"async_complete_streaming_response"
|
||||
] = complete_streaming_response
|
||||
self.model_call_details["async_complete_streaming_response"] = (
|
||||
complete_streaming_response
|
||||
)
|
||||
|
||||
try:
|
||||
if self.model_call_details.get("cache_hit", False) is True:
|
||||
|
|
@ -2573,10 +2573,10 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
model_call_details=self.model_call_details
|
||||
)
|
||||
# base_model defaults to None if not set on model_info
|
||||
self.model_call_details[
|
||||
"response_cost"
|
||||
] = self._response_cost_calculator(
|
||||
result=complete_streaming_response
|
||||
self.model_call_details["response_cost"] = (
|
||||
self._response_cost_calculator(
|
||||
result=complete_streaming_response
|
||||
)
|
||||
)
|
||||
|
||||
verbose_logger.debug(
|
||||
|
|
@ -2593,10 +2593,10 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
)
|
||||
|
||||
## STANDARDIZED LOGGING PAYLOAD
|
||||
self.model_call_details[
|
||||
"standard_logging_object"
|
||||
] = self._build_standard_logging_payload(
|
||||
complete_streaming_response, start_time, end_time
|
||||
self.model_call_details["standard_logging_object"] = (
|
||||
self._build_standard_logging_payload(
|
||||
complete_streaming_response, start_time, end_time
|
||||
)
|
||||
)
|
||||
|
||||
# print standard logging payload
|
||||
|
|
@ -2623,9 +2623,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
# _success_handler_helper_fn
|
||||
if self.model_call_details.get("standard_logging_object") is None:
|
||||
## STANDARDIZED LOGGING PAYLOAD
|
||||
self.model_call_details[
|
||||
"standard_logging_object"
|
||||
] = self._build_standard_logging_payload(result, start_time, end_time)
|
||||
self.model_call_details["standard_logging_object"] = (
|
||||
self._build_standard_logging_payload(result, start_time, end_time)
|
||||
)
|
||||
|
||||
# print standard logging payload
|
||||
if (
|
||||
|
|
@ -2868,18 +2868,18 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
|
||||
## STANDARDIZED LOGGING PAYLOAD
|
||||
|
||||
self.model_call_details[
|
||||
"standard_logging_object"
|
||||
] = get_standard_logging_object_payload(
|
||||
kwargs=self.model_call_details,
|
||||
init_response_obj={},
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
logging_obj=self,
|
||||
status="failure",
|
||||
error_str=str(exception),
|
||||
original_exception=exception,
|
||||
standard_built_in_tools_params=self.standard_built_in_tools_params,
|
||||
self.model_call_details["standard_logging_object"] = (
|
||||
get_standard_logging_object_payload(
|
||||
kwargs=self.model_call_details,
|
||||
init_response_obj={},
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
logging_obj=self,
|
||||
status="failure",
|
||||
error_str=str(exception),
|
||||
original_exception=exception,
|
||||
standard_built_in_tools_params=self.standard_built_in_tools_params,
|
||||
)
|
||||
)
|
||||
return start_time, end_time
|
||||
|
||||
|
|
@ -3849,9 +3849,9 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915
|
|||
service_name=arize_config.project_name,
|
||||
)
|
||||
|
||||
os.environ[
|
||||
"OTEL_EXPORTER_OTLP_TRACES_HEADERS"
|
||||
] = f"space_id={arize_config.space_key or arize_config.space_id},api_key={arize_config.api_key}"
|
||||
os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = (
|
||||
f"space_id={arize_config.space_key or arize_config.space_id},api_key={arize_config.api_key}"
|
||||
)
|
||||
for callback in _in_memory_loggers:
|
||||
if (
|
||||
isinstance(callback, ArizeLogger)
|
||||
|
|
@ -3877,13 +3877,13 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915
|
|||
existing_attrs = os.environ.get("OTEL_RESOURCE_ATTRIBUTES", "")
|
||||
# Add openinference.project.name attribute
|
||||
if existing_attrs:
|
||||
os.environ[
|
||||
"OTEL_RESOURCE_ATTRIBUTES"
|
||||
] = f"{existing_attrs},openinference.project.name={arize_phoenix_config.project_name}"
|
||||
os.environ["OTEL_RESOURCE_ATTRIBUTES"] = (
|
||||
f"{existing_attrs},openinference.project.name={arize_phoenix_config.project_name}"
|
||||
)
|
||||
else:
|
||||
os.environ[
|
||||
"OTEL_RESOURCE_ATTRIBUTES"
|
||||
] = f"openinference.project.name={arize_phoenix_config.project_name}"
|
||||
os.environ["OTEL_RESOURCE_ATTRIBUTES"] = (
|
||||
f"openinference.project.name={arize_phoenix_config.project_name}"
|
||||
)
|
||||
|
||||
# Set Phoenix project name from environment variable
|
||||
phoenix_project_name = os.environ.get("PHOENIX_PROJECT_NAME", None)
|
||||
|
|
@ -3891,19 +3891,19 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915
|
|||
existing_attrs = os.environ.get("OTEL_RESOURCE_ATTRIBUTES", "")
|
||||
# Add openinference.project.name attribute
|
||||
if existing_attrs:
|
||||
os.environ[
|
||||
"OTEL_RESOURCE_ATTRIBUTES"
|
||||
] = f"{existing_attrs},openinference.project.name={phoenix_project_name}"
|
||||
os.environ["OTEL_RESOURCE_ATTRIBUTES"] = (
|
||||
f"{existing_attrs},openinference.project.name={phoenix_project_name}"
|
||||
)
|
||||
else:
|
||||
os.environ[
|
||||
"OTEL_RESOURCE_ATTRIBUTES"
|
||||
] = f"openinference.project.name={phoenix_project_name}"
|
||||
os.environ["OTEL_RESOURCE_ATTRIBUTES"] = (
|
||||
f"openinference.project.name={phoenix_project_name}"
|
||||
)
|
||||
|
||||
# auth can be disabled on local deployments of arize phoenix
|
||||
if arize_phoenix_config.otlp_auth_headers is not None:
|
||||
os.environ[
|
||||
"OTEL_EXPORTER_OTLP_TRACES_HEADERS"
|
||||
] = arize_phoenix_config.otlp_auth_headers
|
||||
os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = (
|
||||
arize_phoenix_config.otlp_auth_headers
|
||||
)
|
||||
|
||||
for callback in _in_memory_loggers:
|
||||
if (
|
||||
|
|
@ -4090,9 +4090,9 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915
|
|||
exporter="otlp_http",
|
||||
endpoint="https://langtrace.ai/api/trace",
|
||||
)
|
||||
os.environ[
|
||||
"OTEL_EXPORTER_OTLP_TRACES_HEADERS"
|
||||
] = f"api_key={os.getenv('LANGTRACE_API_KEY')}"
|
||||
os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = (
|
||||
f"api_key={os.getenv('LANGTRACE_API_KEY')}"
|
||||
)
|
||||
for callback in _in_memory_loggers:
|
||||
if (
|
||||
isinstance(callback, OpenTelemetry)
|
||||
|
|
@ -4987,16 +4987,22 @@ class StandardLoggingPayloadSetup:
|
|||
|
||||
additional_logging_headers: StandardLoggingAdditionalHeaders = {}
|
||||
|
||||
# Populate well-known typed fields with int/str coercion where needed
|
||||
typed_keys: dict = {}
|
||||
for key in StandardLoggingAdditionalHeaders.__annotations__.keys():
|
||||
_key = key.lower()
|
||||
_key = _key.replace("_", "-")
|
||||
_key = key.lower().replace("_", "-")
|
||||
typed_keys[_key] = key
|
||||
if _key in additiona_headers:
|
||||
try:
|
||||
additional_logging_headers[key] = int(additiona_headers[_key]) # type: ignore
|
||||
except (ValueError, TypeError):
|
||||
verbose_logger.debug(
|
||||
f"Could not convert {additiona_headers[_key]} to int for key {key}."
|
||||
)
|
||||
additional_logging_headers[key] = additiona_headers[_key] # type: ignore
|
||||
|
||||
# Preserve all remaining headers verbatim (e.g. llm_provider-x-request-id)
|
||||
for k, v in additiona_headers.items():
|
||||
if k.lower() not in typed_keys:
|
||||
additional_logging_headers[k] = v # type: ignore
|
||||
|
||||
return additional_logging_headers
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -5018,10 +5024,10 @@ class StandardLoggingPayloadSetup:
|
|||
for key in StandardLoggingHiddenParams.__annotations__.keys():
|
||||
if key in hidden_params:
|
||||
if key == "additional_headers":
|
||||
clean_hidden_params[
|
||||
"additional_headers"
|
||||
] = StandardLoggingPayloadSetup.get_additional_headers(
|
||||
hidden_params[key]
|
||||
clean_hidden_params["additional_headers"] = (
|
||||
StandardLoggingPayloadSetup.get_additional_headers(
|
||||
hidden_params[key]
|
||||
)
|
||||
)
|
||||
else:
|
||||
clean_hidden_params[key] = hidden_params[key] # type: ignore
|
||||
|
|
@ -5662,9 +5668,9 @@ def scrub_sensitive_keys_in_metadata(litellm_params: Optional[dict]):
|
|||
):
|
||||
for k, v in metadata["user_api_key_metadata"].items():
|
||||
if k == "logging": # prevent logging user logging keys
|
||||
cleaned_user_api_key_metadata[
|
||||
k
|
||||
] = "scrubbed_by_litellm_for_sensitive_keys"
|
||||
cleaned_user_api_key_metadata[k] = (
|
||||
"scrubbed_by_litellm_for_sensitive_keys"
|
||||
)
|
||||
else:
|
||||
cleaned_user_api_key_metadata[k] = v
|
||||
|
||||
|
|
|
|||
|
|
@ -2647,6 +2647,8 @@ class StandardLoggingAdditionalHeaders(TypedDict, total=False):
|
|||
x_ratelimit_limit_tokens: int
|
||||
x_ratelimit_remaining_requests: int
|
||||
x_ratelimit_remaining_tokens: int
|
||||
x_ratelimit_reset_requests: str
|
||||
x_ratelimit_reset_tokens: str
|
||||
|
||||
|
||||
class StandardLoggingHiddenParams(TypedDict):
|
||||
|
|
|
|||
|
|
@ -158,12 +158,15 @@ def test_get_additional_headers():
|
|||
additional_logging_headers = StandardLoggingPayloadSetup.get_additional_headers(
|
||||
additional_headers
|
||||
)
|
||||
assert additional_logging_headers == {
|
||||
"x_ratelimit_limit_requests": 2000,
|
||||
"x_ratelimit_remaining_requests": 1999,
|
||||
"x_ratelimit_limit_tokens": 160000,
|
||||
"x_ratelimit_remaining_tokens": 160000,
|
||||
}
|
||||
# Typed rate-limit fields are coerced to int
|
||||
assert additional_logging_headers is not None
|
||||
assert additional_logging_headers.get("x_ratelimit_limit_requests") == 2000
|
||||
assert additional_logging_headers.get("x_ratelimit_remaining_requests") == 1999
|
||||
assert additional_logging_headers.get("x_ratelimit_limit_tokens") == 160000
|
||||
assert additional_logging_headers.get("x_ratelimit_remaining_tokens") == 160000
|
||||
# Provider-specific headers are preserved verbatim (not dropped)
|
||||
assert additional_logging_headers.get("llm_provider-request-id") == "req_01F6CycZZPSHKRCCctcS1Vto"
|
||||
assert additional_logging_headers.get("llm_provider-anthropic-ratelimit-requests-reset") == "2024-10-29T23:57:40Z"
|
||||
|
||||
|
||||
def all_fields_present(standard_logging_metadata: StandardLoggingMetadata):
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ sys.path.insert(
|
|||
import time
|
||||
|
||||
from litellm.constants import SENTRY_DENYLIST, SENTRY_PII_DENYLIST
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
Logging as LitellmLogging
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LitellmLogging
|
||||
from litellm.litellm_core_utils.litellm_logging import set_callbacks
|
||||
from litellm.types.utils import ModelResponse, TextCompletionResponse
|
||||
|
||||
|
|
@ -140,8 +139,7 @@ def test_sentry_environment():
|
|||
|
||||
|
||||
def test_use_custom_pricing_for_model():
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
use_custom_pricing_for_model
|
||||
from litellm.litellm_core_utils.litellm_logging import use_custom_pricing_for_model
|
||||
|
||||
litellm_params = {
|
||||
"custom_llm_provider": "azure",
|
||||
|
|
@ -156,8 +154,7 @@ def test_use_custom_pricing_for_model_via_litellm_metadata():
|
|||
Generic API call routes (/messages, /responses) store model_info
|
||||
under litellm_metadata, not metadata. Regression test for #23185.
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
use_custom_pricing_for_model
|
||||
from litellm.litellm_core_utils.litellm_logging import use_custom_pricing_for_model
|
||||
|
||||
litellm_params = {
|
||||
"litellm_metadata": {
|
||||
|
|
@ -173,8 +170,7 @@ def test_use_custom_pricing_for_model_via_litellm_metadata():
|
|||
|
||||
def test_use_custom_pricing_not_detected_litellm_metadata_no_pricing():
|
||||
"""Should return False when litellm_metadata.model_info has no pricing keys."""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
use_custom_pricing_for_model
|
||||
from litellm.litellm_core_utils.litellm_logging import use_custom_pricing_for_model
|
||||
|
||||
litellm_params = {
|
||||
"litellm_metadata": {
|
||||
|
|
@ -190,8 +186,7 @@ def test_response_cost_calculator_uses_router_model_id_from_litellm_metadata():
|
|||
does not carry _hidden_params (e.g. ResponsesAPIResponse from /v1/responses
|
||||
streaming). Regression test for custom pricing on streaming responses."""
|
||||
import litellm
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
Logging as LiteLLMLoggingObj
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.types.llms.openai import ResponsesAPIResponse
|
||||
|
||||
custom_model_id = "gpt-5-custom-pricing"
|
||||
|
|
@ -301,8 +296,9 @@ class TestGetRouterModelId:
|
|||
|
||||
def test_returns_none_when_no_litellm_params(self):
|
||||
"""Should return None when litellm_params is not set."""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
Logging as LiteLLMLoggingObj
|
||||
from litellm.litellm_core_utils.litellm_logging import (
|
||||
Logging as LiteLLMLoggingObj,
|
||||
)
|
||||
|
||||
obj = LiteLLMLoggingObj(
|
||||
model="test",
|
||||
|
|
@ -326,10 +322,12 @@ class TestAnthropicPassthroughCustomPricing:
|
|||
when the logging object carries custom pricing in model_info."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
Logging as LiteLLMLoggingObj
|
||||
from litellm.proxy.pass_through_endpoints.llm_provider_handlers.anthropic_passthrough_logging_handler import \
|
||||
AnthropicPassthroughLoggingHandler
|
||||
from litellm.litellm_core_utils.litellm_logging import (
|
||||
Logging as LiteLLMLoggingObj,
|
||||
)
|
||||
from litellm.proxy.pass_through_endpoints.llm_provider_handlers.anthropic_passthrough_logging_handler import (
|
||||
AnthropicPassthroughLoggingHandler,
|
||||
)
|
||||
|
||||
logging_obj = LiteLLMLoggingObj(
|
||||
model="claude-sonnet-4-20250514",
|
||||
|
|
@ -438,7 +436,10 @@ class TestUpdateFromKwargs:
|
|||
)
|
||||
|
||||
# kwargs metadata is preserved, caller metadata is merged in
|
||||
assert logging_obj.litellm_params["metadata"] == {"from_kwargs": True, "from_caller": True}
|
||||
assert logging_obj.litellm_params["metadata"] == {
|
||||
"from_kwargs": True,
|
||||
"from_caller": True,
|
||||
}
|
||||
|
||||
def test_kwargs_metadata_wins_over_caller_metadata_in_conflict(self, logging_obj):
|
||||
"""kwargs metadata takes precedence; caller litellm_params metadata is merged without overwriting."""
|
||||
|
|
@ -446,7 +447,10 @@ class TestUpdateFromKwargs:
|
|||
|
||||
logging_obj.update_from_kwargs(
|
||||
kwargs=kwargs,
|
||||
litellm_params={"metadata": {"from_caller": True, "shared_key": "caller_value"}, "litellm_call_id": "x"},
|
||||
litellm_params={
|
||||
"metadata": {"from_caller": True, "shared_key": "caller_value"},
|
||||
"litellm_call_id": "x",
|
||||
},
|
||||
)
|
||||
|
||||
# kwargs metadata is preserved (shared_key keeps the kwargs value), caller-only keys are added
|
||||
|
|
@ -458,8 +462,9 @@ class TestUpdateFromKwargs:
|
|||
|
||||
def test_custom_pricing_detected_via_litellm_metadata(self, logging_obj):
|
||||
"""Custom pricing in litellm_metadata.model_info should set custom_pricing flag."""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
use_custom_pricing_for_model
|
||||
from litellm.litellm_core_utils.litellm_logging import (
|
||||
use_custom_pricing_for_model,
|
||||
)
|
||||
|
||||
lm_meta = {
|
||||
"model_info": {
|
||||
|
|
@ -518,8 +523,7 @@ async def test_datadog_logger_not_shadowed_by_llm_obs(monkeypatch):
|
|||
monkeypatch.setenv("DD_SITE", "us5.datadoghq.com")
|
||||
|
||||
from litellm.integrations.datadog.datadog import DataDogLogger
|
||||
from litellm.integrations.datadog.datadog_llm_obs import \
|
||||
DataDogLLMObsLogger
|
||||
from litellm.integrations.datadog.datadog_llm_obs import DataDogLLMObsLogger
|
||||
from litellm.litellm_core_utils import litellm_logging as logging_module
|
||||
|
||||
logging_module._in_memory_loggers.clear()
|
||||
|
|
@ -560,8 +564,7 @@ async def test_logfire_logger_accepts_env_vars_for_base_url(monkeypatch):
|
|||
) # no trailing slash on purpose
|
||||
|
||||
# Import after env vars are set (important if module-level caching exists)
|
||||
from litellm.integrations.opentelemetry import \
|
||||
OpenTelemetry # logger class
|
||||
from litellm.integrations.opentelemetry import OpenTelemetry # logger class
|
||||
from litellm.litellm_core_utils import litellm_logging as logging_module
|
||||
|
||||
logging_module._in_memory_loggers.clear()
|
||||
|
|
@ -890,8 +893,7 @@ def test_success_handler_runs_guardrail_logging_hook_when_enabled(logging_obj):
|
|||
|
||||
|
||||
def test_get_user_agent_tags():
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
tags = StandardLoggingPayloadSetup._get_user_agent_tags(
|
||||
proxy_server_request={
|
||||
|
|
@ -906,8 +908,7 @@ def test_get_user_agent_tags():
|
|||
|
||||
|
||||
def test_get_request_tags():
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
tags = StandardLoggingPayloadSetup._get_request_tags(
|
||||
litellm_params={"metadata": {"tags": ["test-tag"]}},
|
||||
|
|
@ -934,8 +935,7 @@ def test_get_request_tags_from_metadata_and_litellm_metadata():
|
|||
4. No tags in either
|
||||
5. None values for metadata/litellm_metadata
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Test case 1: Tags in metadata only
|
||||
tags = StandardLoggingPayloadSetup._get_request_tags(
|
||||
|
|
@ -1016,8 +1016,7 @@ def test_get_request_tags_does_not_mutate_original_tags():
|
|||
would cause User-Agent tags to be duplicated because the function was mutating
|
||||
the original tags list instead of creating a copy.
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Create metadata with original tags
|
||||
original_tags = ["custom-tag-1", "custom-tag-2"]
|
||||
|
|
@ -1077,8 +1076,7 @@ def test_get_request_tags_does_not_mutate_original_tags():
|
|||
def test_get_extra_header_tags():
|
||||
"""Test the _get_extra_header_tags method with various scenarios."""
|
||||
import litellm
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Store original value to restore later
|
||||
original_extra_headers = getattr(litellm, "extra_spend_tag_headers", None)
|
||||
|
|
@ -1299,17 +1297,17 @@ async def test_e2e_generate_cold_storage_object_key_successful():
|
|||
from datetime import datetime, timezone
|
||||
from unittest.mock import patch
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Create test data
|
||||
start_time = datetime(2025, 1, 15, 10, 30, 45, 123456, timezone.utc)
|
||||
response_id = "chatcmpl-test-12345"
|
||||
team_alias = "test-team"
|
||||
|
||||
with patch("litellm.cold_storage_custom_logger", return_value="s3"), patch(
|
||||
"litellm.integrations.s3.get_s3_object_key"
|
||||
) as mock_get_s3_key:
|
||||
with (
|
||||
patch("litellm.cold_storage_custom_logger", return_value="s3"),
|
||||
patch("litellm.integrations.s3.get_s3_object_key") as mock_get_s3_key,
|
||||
):
|
||||
# Mock the S3 object key generation to return a predictable result
|
||||
mock_get_s3_key.return_value = (
|
||||
"2025-01-15/time-10-30-45-123456_chatcmpl-test-12345.json"
|
||||
|
|
@ -1342,8 +1340,7 @@ async def test_e2e_generate_cold_storage_object_key_with_custom_logger_s3_path()
|
|||
from datetime import datetime, timezone
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Create test data
|
||||
start_time = datetime(2025, 1, 15, 10, 30, 45, 123456, timezone.utc)
|
||||
|
|
@ -1353,11 +1350,13 @@ async def test_e2e_generate_cold_storage_object_key_with_custom_logger_s3_path()
|
|||
mock_custom_logger = MagicMock()
|
||||
mock_custom_logger.s3_path = "storage"
|
||||
|
||||
with patch("litellm.cold_storage_custom_logger", "s3_v2"), patch(
|
||||
"litellm.logging_callback_manager.get_active_custom_logger_for_callback_name"
|
||||
) as mock_get_logger, patch(
|
||||
"litellm.integrations.s3.get_s3_object_key"
|
||||
) as mock_get_s3_key:
|
||||
with (
|
||||
patch("litellm.cold_storage_custom_logger", "s3_v2"),
|
||||
patch(
|
||||
"litellm.logging_callback_manager.get_active_custom_logger_for_callback_name"
|
||||
) as mock_get_logger,
|
||||
patch("litellm.integrations.s3.get_s3_object_key") as mock_get_s3_key,
|
||||
):
|
||||
# Setup mocks
|
||||
mock_get_logger.return_value = mock_custom_logger
|
||||
mock_get_s3_key.return_value = (
|
||||
|
|
@ -1394,8 +1393,7 @@ async def test_e2e_generate_cold_storage_object_key_with_logger_no_s3_path():
|
|||
from datetime import datetime, timezone
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Create test data
|
||||
start_time = datetime(2025, 1, 15, 10, 30, 45, 123456, timezone.utc)
|
||||
|
|
@ -1405,11 +1403,13 @@ async def test_e2e_generate_cold_storage_object_key_with_logger_no_s3_path():
|
|||
mock_custom_logger = MagicMock()
|
||||
mock_custom_logger.s3_path = None # or could be missing attribute
|
||||
|
||||
with patch("litellm.cold_storage_custom_logger", "s3_v2"), patch(
|
||||
"litellm.logging_callback_manager.get_active_custom_logger_for_callback_name"
|
||||
) as mock_get_logger, patch(
|
||||
"litellm.integrations.s3.get_s3_object_key"
|
||||
) as mock_get_s3_key:
|
||||
with (
|
||||
patch("litellm.cold_storage_custom_logger", "s3_v2"),
|
||||
patch(
|
||||
"litellm.logging_callback_manager.get_active_custom_logger_for_callback_name"
|
||||
) as mock_get_logger,
|
||||
patch("litellm.integrations.s3.get_s3_object_key") as mock_get_s3_key,
|
||||
):
|
||||
# Setup mocks
|
||||
mock_get_logger.return_value = mock_custom_logger
|
||||
mock_get_s3_key.return_value = (
|
||||
|
|
@ -1442,8 +1442,7 @@ async def test_e2e_generate_cold_storage_object_key_not_configured():
|
|||
from unittest.mock import patch
|
||||
|
||||
import litellm
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Create test data
|
||||
start_time = datetime(2025, 1, 15, 10, 30, 45, 123456, timezone.utc)
|
||||
|
|
@ -1467,8 +1466,7 @@ def test_get_final_response_obj_with_empty_response_obj_and_list_init():
|
|||
|
||||
When response_obj is empty (falsy), the method should return init_response_obj if it's a list.
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Create test objects
|
||||
class TestObject1:
|
||||
|
|
@ -1504,8 +1502,7 @@ def test_get_usage_as_dict():
|
|||
"""
|
||||
Test get_usage_as_dict returns usage as plain dict from response_obj or combined_usage_object.
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
from litellm.types.utils import Usage
|
||||
|
||||
# Test case 1: None response_obj returns empty usage dict
|
||||
|
|
@ -1543,8 +1540,7 @@ def test_append_system_prompt_messages():
|
|||
"""
|
||||
Test append_system_prompt_messages prepends system message from kwargs to messages list.
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Test case 1: system in kwargs with existing messages
|
||||
kwargs = {"system": "You are a helpful assistant"}
|
||||
|
|
@ -1615,8 +1611,7 @@ async def test_async_success_handler_sets_standard_logging_object_for_pass_throu
|
|||
from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
Logging as LiteLLMLoggingObj
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.types.utils import StandardPassThroughResponseObject
|
||||
|
||||
# Create a logging object for a pass-through endpoint
|
||||
|
|
@ -1697,8 +1692,7 @@ async def test_async_success_handler_prevents_reprocessing_for_pass_through_endp
|
|||
from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
Logging as LiteLLMLoggingObj
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.types.utils import StandardPassThroughResponseObject
|
||||
|
||||
# Create a logging object for a pass-through endpoint
|
||||
|
|
@ -1774,8 +1768,7 @@ async def test_async_success_handler_sets_standard_logging_object_for_streaming_
|
|||
from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
Logging as LiteLLMLoggingObj
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.types.utils import StandardPassThroughResponseObject
|
||||
|
||||
# Create a logging object for a streaming pass-through endpoint
|
||||
|
|
@ -1831,8 +1824,7 @@ def test_get_error_information_error_code_priority():
|
|||
Test get_error_information prioritizes 'code' attribute over 'status_code' attribute
|
||||
and handles edge cases like empty strings and "None" string values.
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
StandardLoggingPayloadSetup
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
# Test case 1: Exception with 'code' attribute (ProxyException style)
|
||||
class ProxyException(Exception):
|
||||
|
|
@ -2025,8 +2017,7 @@ async def test_async_success_handler_preserves_response_cost_for_pass_through_en
|
|||
by pass-through handlers (Gemini/Vertex)."""
|
||||
from datetime import datetime
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import \
|
||||
Logging as LiteLLMLoggingObj
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.types.utils import ModelResponse, Usage
|
||||
|
||||
logging_obj = LiteLLMLoggingObj(
|
||||
|
|
@ -2366,6 +2357,56 @@ def test_merge_hidden_params_from_response_into_metadata_no_op_when_empty():
|
|||
_hidden_params = {}
|
||||
|
||||
logging_obj._merge_hidden_params_from_response_into_metadata(_NoHp())
|
||||
assert "hidden_params" not in logging_obj.model_call_details["litellm_params"][
|
||||
"metadata"
|
||||
]
|
||||
assert (
|
||||
"hidden_params"
|
||||
not in logging_obj.model_call_details["litellm_params"]["metadata"]
|
||||
)
|
||||
|
||||
|
||||
# ── StandardLoggingPayloadSetup.get_additional_headers ───────────────────────
|
||||
|
||||
|
||||
def test_get_additional_headers_preserves_provider_request_id():
|
||||
"""llm_provider-x-request-id must survive the get_additional_headers filter."""
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
raw = {
|
||||
"x-ratelimit-remaining-requests": "29999",
|
||||
"x-ratelimit-remaining-tokens": "149999970",
|
||||
"llm_provider-x-request-id": "req_85f49b546c7b4d3180755621f36631a1",
|
||||
"llm_provider-openai-organization": "my-org",
|
||||
"llm_provider-openai-processing-ms": "649",
|
||||
}
|
||||
|
||||
result = StandardLoggingPayloadSetup.get_additional_headers(raw)
|
||||
|
||||
assert result is not None
|
||||
# well-known fields parsed as ints
|
||||
assert result["x_ratelimit_remaining_requests"] == 29999 # type: ignore
|
||||
assert result["x_ratelimit_remaining_tokens"] == 149999970 # type: ignore
|
||||
# provider-specific headers must be preserved verbatim
|
||||
assert result["llm_provider-x-request-id"] == "req_85f49b546c7b4d3180755621f36631a1" # type: ignore
|
||||
assert result["llm_provider-openai-organization"] == "my-org" # type: ignore
|
||||
assert result["llm_provider-openai-processing-ms"] == "649" # type: ignore
|
||||
|
||||
|
||||
def test_get_additional_headers_returns_none_for_none_input():
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
assert StandardLoggingPayloadSetup.get_additional_headers(None) is None
|
||||
|
||||
|
||||
def test_get_additional_headers_reset_fields_preserved():
|
||||
"""x-ratelimit-reset-* fields (added to the TypedDict) must be captured."""
|
||||
from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup
|
||||
|
||||
raw = {
|
||||
"x-ratelimit-reset-requests": "1s",
|
||||
"x-ratelimit-reset-tokens": "100ms",
|
||||
}
|
||||
|
||||
result = StandardLoggingPayloadSetup.get_additional_headers(raw)
|
||||
|
||||
assert result is not None
|
||||
assert result["x_ratelimit_reset_requests"] == "1s" # type: ignore
|
||||
assert result["x_ratelimit_reset_tokens"] == "100ms" # type: ignore
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue