mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-15 06:19:29 +00:00
raise ValueError on os.environ/ references in request-supplied callback params
Previously these were silently dropped with a verbose warning, which could break observability integrations without surfacing a clear error. Now raises ValueError with remediation steps (configure server-side or pass the resolved value) so callers get immediate, actionable feedback.
This commit is contained in:
@@ -1,12 +1,28 @@
|
||||
from typing import Dict, Optional
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.types.utils import StandardCallbackDynamicParams
|
||||
|
||||
|
||||
def _is_env_reference(value: object) -> bool:
|
||||
return isinstance(value, str) and "os.environ/" in value
|
||||
|
||||
|
||||
def _raise_env_reference_error(param: str, *, source: str) -> None:
|
||||
raise ValueError(
|
||||
f"Callback param '{param}' (from {source}) contains an 'os.environ/' "
|
||||
"reference. Environment references in request-supplied parameters are "
|
||||
"no longer resolved server-side for security reasons.\n"
|
||||
"To resolve:\n"
|
||||
" 1. Remove the 'os.environ/' reference from your request body / "
|
||||
"metadata.\n"
|
||||
" 2. Either (a) configure this callback value in your proxy "
|
||||
"config.yaml under 'litellm_settings' / 'general_settings', or "
|
||||
"(b) pass the resolved secret value directly in the request.\n"
|
||||
"See https://docs.litellm.ai/docs/proxy/logging for server-side "
|
||||
"callback configuration."
|
||||
)
|
||||
|
||||
|
||||
# Hardcoded list of supported callback params to avoid runtime inspection issues with TypedDict
|
||||
_supported_callback_params = [
|
||||
"langfuse_public_key",
|
||||
@@ -52,13 +68,7 @@ def initialize_standard_callback_dynamic_params(
|
||||
if param in kwargs:
|
||||
_param_value = kwargs.get(param)
|
||||
if _is_env_reference(_param_value):
|
||||
verbose_logger.warning(
|
||||
"Dropping callback param '%s': os.environ/ references "
|
||||
"in request-supplied parameters are not resolved. "
|
||||
"Configure this value server-side instead.",
|
||||
param,
|
||||
)
|
||||
continue
|
||||
_raise_env_reference_error(param, source="request body")
|
||||
standard_callback_dynamic_params[param] = _param_value # type: ignore
|
||||
|
||||
# 2. Fallback: check "metadata" or "litellm_params" -> "metadata"
|
||||
@@ -72,14 +82,7 @@ def initialize_standard_callback_dynamic_params(
|
||||
if param not in standard_callback_dynamic_params and param in metadata:
|
||||
_param_value = metadata.get(param)
|
||||
if _is_env_reference(_param_value):
|
||||
verbose_logger.warning(
|
||||
"Dropping callback param '%s' from metadata: "
|
||||
"os.environ/ references in request-supplied "
|
||||
"parameters are not resolved. Configure this "
|
||||
"value server-side instead.",
|
||||
param,
|
||||
)
|
||||
continue
|
||||
_raise_env_reference_error(param, source="metadata")
|
||||
standard_callback_dynamic_params[param] = _param_value # type: ignore
|
||||
|
||||
return standard_callback_dynamic_params
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../../.."))
|
||||
|
||||
from litellm.litellm_core_utils.initialize_dynamic_callback_params import (
|
||||
initialize_standard_callback_dynamic_params,
|
||||
)
|
||||
|
||||
|
||||
def test_resolves_plain_values_at_top_level():
|
||||
kwargs = {
|
||||
"langfuse_public_key": "pk-test",
|
||||
"langfuse_secret_key": "sk-test",
|
||||
}
|
||||
|
||||
params = initialize_standard_callback_dynamic_params(kwargs)
|
||||
|
||||
assert params.get("langfuse_public_key") == "pk-test"
|
||||
assert params.get("langfuse_secret_key") == "sk-test"
|
||||
|
||||
|
||||
def test_resolves_plain_values_from_metadata():
|
||||
kwargs = {
|
||||
"metadata": {
|
||||
"langfuse_public_key": "pk-meta",
|
||||
"langfuse_host": "https://test.langfuse.com",
|
||||
}
|
||||
}
|
||||
|
||||
params = initialize_standard_callback_dynamic_params(kwargs)
|
||||
|
||||
assert params.get("langfuse_public_key") == "pk-meta"
|
||||
assert params.get("langfuse_host") == "https://test.langfuse.com"
|
||||
|
||||
|
||||
def test_env_reference_at_top_level_raises_with_guidance():
|
||||
kwargs = {"langfuse_public_key": "os.environ/LANGFUSE_PUBLIC_KEY"}
|
||||
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
initialize_standard_callback_dynamic_params(kwargs)
|
||||
|
||||
message = str(exc_info.value)
|
||||
assert "langfuse_public_key" in message
|
||||
assert "request body" in message
|
||||
assert "os.environ/" in message
|
||||
assert "config.yaml" in message
|
||||
|
||||
|
||||
def test_env_reference_in_metadata_raises_with_guidance():
|
||||
kwargs = {
|
||||
"metadata": {
|
||||
"langsmith_api_key": "os.environ/LANGSMITH_API_KEY",
|
||||
}
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
initialize_standard_callback_dynamic_params(kwargs)
|
||||
|
||||
message = str(exc_info.value)
|
||||
assert "langsmith_api_key" in message
|
||||
assert "metadata" in message
|
||||
|
||||
|
||||
def test_env_reference_in_litellm_params_metadata_raises():
|
||||
kwargs = {
|
||||
"litellm_params": {
|
||||
"metadata": {
|
||||
"gcs_bucket_name": "os.environ/GCS_BUCKET",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
initialize_standard_callback_dynamic_params(kwargs)
|
||||
|
||||
assert "gcs_bucket_name" in str(exc_info.value)
|
||||
|
||||
|
||||
def test_non_string_values_are_not_flagged():
|
||||
kwargs = {
|
||||
"langsmith_sampling_rate": 0.5,
|
||||
"turn_off_message_logging": True,
|
||||
}
|
||||
|
||||
params = initialize_standard_callback_dynamic_params(kwargs)
|
||||
|
||||
assert params.get("langsmith_sampling_rate") == 0.5
|
||||
assert params.get("turn_off_message_logging") is True
|
||||
|
||||
|
||||
def test_empty_kwargs_returns_empty_params():
|
||||
params = initialize_standard_callback_dynamic_params(None)
|
||||
assert dict(params) == {}
|
||||
|
||||
params = initialize_standard_callback_dynamic_params({})
|
||||
assert dict(params) == {}
|
||||
Reference in New Issue
Block a user