mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
fix(otel): keep credentialless fallback on base path
This commit is contained in:
parent
c46341a1fd
commit
c12da6dc90
2 changed files with 28 additions and 24 deletions
|
|
@ -4803,17 +4803,11 @@ def _maybe_construct_otel_v2(callback_name: str, _in_memory_loggers: list[Custom
|
|||
``callback_name`` — callers should then fall through to the legacy path.
|
||||
|
||||
A preset that needs operator credentials it cannot find is allowed to build
|
||||
anyway, exporting nowhere, only while this request has a key/team destination
|
||||
for that backend: the exporter-less logger exists to let the fan-out carry those
|
||||
spans without a second detached copy. With no such destination the preset raises
|
||||
as it always did and the caller falls through to the legacy path, so the proxy
|
||||
never publishes a provider that exports nowhere for a backend the operator
|
||||
configured and no tenant can use.
|
||||
|
||||
The degraded preset keeps the operator's generic OTLP collector, so a proxy whose
|
||||
only v2 backend is that preset still reaches it. Beside another v2 logger the
|
||||
collector is already that logger's, and a second copy of every model span from
|
||||
this one would land there too, so only the credential-gated exporter is kept.
|
||||
only when this request has a key/team destination for that backend and another
|
||||
V2 logger is already registered to carry the fan-out. The resulting logger keeps
|
||||
only its credential-gated exporter, while the registered logger owns operator
|
||||
delivery. Without that carrier, the preset raises as it always did and the
|
||||
caller falls through to the legacy path.
|
||||
"""
|
||||
from litellm.integrations.otel.model.config import is_otel_v2_enabled
|
||||
|
||||
|
|
@ -4827,6 +4821,7 @@ def _maybe_construct_otel_v2(callback_name: str, _in_memory_loggers: list[Custom
|
|||
if preset_fn is None:
|
||||
return None
|
||||
serves_a_destination: Final = callback_name in destination_backends()
|
||||
has_v2_logger: Final = any(isinstance(callback, OpenTelemetryV2) for callback in _in_memory_loggers)
|
||||
for callback in _in_memory_loggers:
|
||||
if (
|
||||
isinstance(callback, OpenTelemetryV2)
|
||||
|
|
@ -4835,16 +4830,12 @@ def _maybe_construct_otel_v2(callback_name: str, _in_memory_loggers: list[Custom
|
|||
):
|
||||
return callback
|
||||
try:
|
||||
built: Final = preset_fn(allow_missing_credentials=serves_a_destination)
|
||||
built: Final = preset_fn(allow_missing_credentials=serves_a_destination and has_v2_logger)
|
||||
except Exception:
|
||||
# If env vars are missing or the preset raises, defer to the legacy path
|
||||
# so customers get the same error story they had before V2 landed.
|
||||
return None
|
||||
config: Final = (
|
||||
_only_the_gated_exporter(built)
|
||||
if _is_credential_gated(built) and any(isinstance(callback, OpenTelemetryV2) for callback in _in_memory_loggers)
|
||||
else built
|
||||
)
|
||||
config: Final = _only_the_gated_exporter(built) if _is_credential_gated(built) else built
|
||||
if _exports_nowhere(config):
|
||||
verbose_logger.warning(
|
||||
"OTel V2: no operator credentials for '%s'; only key/team destinations will receive its traces",
|
||||
|
|
|
|||
|
|
@ -1123,21 +1123,22 @@ class TestPresetDegradation:
|
|||
with pytest.raises(ValueError, match="LANGFUSE_PUBLIC_KEY"):
|
||||
langfuse_preset()
|
||||
|
||||
def test_a_credential_less_proxy_builds_the_v2_logger_for_a_team_destination(self, monkeypatch):
|
||||
def test_a_credential_less_proxy_builds_the_gated_logger_beside_a_v2_carrier(self, monkeypatch):
|
||||
from litellm.litellm_core_utils.litellm_logging import _maybe_construct_otel_v2
|
||||
|
||||
credential_less_proxy(monkeypatch)
|
||||
monkeypatch.setenv("LITELLM_OTEL_V2", "true")
|
||||
carrier = build_otel_v2_logger(OpenTelemetryV2Config(exporter="in_memory"))
|
||||
|
||||
def run():
|
||||
set_request_destinations((LANGFUSE_DEST,))
|
||||
return _maybe_construct_otel_v2("langfuse_otel", [])
|
||||
return _maybe_construct_otel_v2("langfuse_otel", [carrier])
|
||||
|
||||
is_otel_v2_enabled.cache_clear()
|
||||
logger = in_fresh_context(run)
|
||||
is_otel_v2_enabled.cache_clear()
|
||||
|
||||
assert logger is not None, "team-only deployments must not fall back to the legacy integration"
|
||||
assert logger is not None
|
||||
assert all(spec.requires_headers and not spec.headers for spec in logger.config.exporters)
|
||||
|
||||
def test_a_credential_less_proxy_with_no_destinations_falls_back_to_the_legacy_path(self, monkeypatch):
|
||||
|
|
@ -1179,7 +1180,7 @@ class TestPresetDegradation:
|
|||
|
||||
credential_less_proxy(monkeypatch)
|
||||
monkeypatch.setenv("LITELLM_OTEL_V2", "true")
|
||||
loggers = []
|
||||
loggers = [build_otel_v2_logger(OpenTelemetryV2Config(exporter="in_memory"))]
|
||||
|
||||
def with_destination():
|
||||
set_request_destinations((LANGFUSE_DEST,))
|
||||
|
|
@ -1237,10 +1238,21 @@ class TestPresetDegradation:
|
|||
assert [spec.endpoint for spec in logger.config.exporters] == [None]
|
||||
assert all(spec.requires_headers and not spec.headers for spec in logger.config.exporters)
|
||||
|
||||
def test_a_degraded_logger_on_its_own_keeps_the_operator_collector(self, monkeypatch):
|
||||
logger = self._degraded_langfuse_beside([], monkeypatch)
|
||||
def test_a_credential_less_proxy_with_a_destination_but_no_v2_carrier_falls_back(self, monkeypatch):
|
||||
from litellm.litellm_core_utils.litellm_logging import _maybe_construct_otel_v2
|
||||
|
||||
assert [spec.endpoint for spec in logger.config.exporters] == ["http://collector.local:4318", None]
|
||||
credential_less_proxy(monkeypatch)
|
||||
monkeypatch.setenv("LITELLM_OTEL_V2", "true")
|
||||
|
||||
def run():
|
||||
set_request_destinations((LANGFUSE_DEST,))
|
||||
return _maybe_construct_otel_v2("langfuse_otel", [])
|
||||
|
||||
is_otel_v2_enabled.cache_clear()
|
||||
logger = in_fresh_context(run)
|
||||
is_otel_v2_enabled.cache_clear()
|
||||
|
||||
assert logger is None
|
||||
|
||||
def test_a_credentialed_logger_beside_another_v2_logger_keeps_every_exporter(self, monkeypatch):
|
||||
"""Only a degraded preset gives the collector up; an operator who configured
|
||||
|
|
@ -1249,6 +1261,7 @@ class TestPresetDegradation:
|
|||
|
||||
monkeypatch.setenv("LANGFUSE_PUBLIC_KEY", "pk-lf-1")
|
||||
monkeypatch.setenv("LANGFUSE_SECRET_KEY", "sk-lf-1")
|
||||
monkeypatch.setenv("LANGFUSE_HOST", "https://cloud.langfuse.com")
|
||||
monkeypatch.setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://collector.local:4318")
|
||||
monkeypatch.setenv("LITELLM_OTEL_V2", "true")
|
||||
collector_logger = build_otel_v2_logger(OpenTelemetryV2Config(exporter="in_memory"))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue