mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
refactor(otel): share the console placeholder check with the presets
This commit is contained in:
parent
88526c39c6
commit
4372884404
3 changed files with 17 additions and 14 deletions
|
|
@ -32,12 +32,12 @@ def credential_gated_exporters(
|
|||
override filter still recognises which backend this provider speaks for.
|
||||
"""
|
||||
return (
|
||||
*(spec for spec in exporters if not _is_unconfigured_placeholder(spec)),
|
||||
*(spec for spec in exporters if not is_unconfigured_placeholder(spec)),
|
||||
ExporterSpec(owner=owner, requires_headers=True),
|
||||
)
|
||||
|
||||
|
||||
def _is_unconfigured_placeholder(spec: "ExporterSpec") -> bool:
|
||||
def is_unconfigured_placeholder(spec: "ExporterSpec") -> bool:
|
||||
"""Whether ``spec`` is the one ``_normalize`` folds in when nothing was configured.
|
||||
|
||||
Every field at its default is what says the operator asked for nothing: an exporter
|
||||
|
|
|
|||
|
|
@ -4806,8 +4806,9 @@ def _maybe_construct_otel_v2(callback_name: str, _in_memory_loggers: list[Custom
|
|||
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.
|
||||
delivery. Without that carrier, a preset that raises or that ends up with nothing
|
||||
but its gated exporter and the default console placeholder returns ``None``, so the
|
||||
caller falls through to the legacy path exactly as before V2 landed.
|
||||
"""
|
||||
from litellm.integrations.otel.model.config import is_otel_v2_enabled
|
||||
|
||||
|
|
@ -4822,6 +4823,7 @@ def _maybe_construct_otel_v2(callback_name: str, _in_memory_loggers: list[Custom
|
|||
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)
|
||||
carried: Final = serves_a_destination and has_v2_logger
|
||||
for callback in _in_memory_loggers:
|
||||
if (
|
||||
isinstance(callback, OpenTelemetryV2)
|
||||
|
|
@ -4830,20 +4832,15 @@ 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 and has_v2_logger)
|
||||
built: Final = preset_fn(allow_missing_credentials=carried)
|
||||
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
|
||||
gated: Final = _is_credential_gated(built)
|
||||
has_operator_exporter: Final = any(
|
||||
not _is_gated(spec) and bool(spec.model_dump(exclude_defaults=True)) for spec in built.exporters
|
||||
)
|
||||
if _exports_nowhere(built) and not (serves_a_destination and has_v2_logger):
|
||||
if gated and not carried and not _has_operator_exporter(built):
|
||||
return None
|
||||
if gated and not has_operator_exporter and not (serves_a_destination and has_v2_logger):
|
||||
return None
|
||||
config: Final = _only_the_gated_exporter(built) if gated and serves_a_destination and has_v2_logger else built
|
||||
config: Final = _only_the_gated_exporter(built) if gated and carried else built
|
||||
if _exports_nowhere(config):
|
||||
verbose_logger.warning(
|
||||
"OTel V2: no operator credentials for '%s'; only key/team destinations will receive its traces",
|
||||
|
|
@ -4864,6 +4861,13 @@ def _is_credential_gated(config: "OpenTelemetryV2Config") -> bool:
|
|||
return any(_is_gated(spec) for spec in config.exporters)
|
||||
|
||||
|
||||
def _has_operator_exporter(config: "OpenTelemetryV2Config") -> bool:
|
||||
"""Whether the operator configured somewhere real to export, beyond the default console placeholder."""
|
||||
from litellm.integrations.otel.presets.utils import is_unconfigured_placeholder
|
||||
|
||||
return any(not _is_gated(spec) and not is_unconfigured_placeholder(spec) for spec in config.exporters)
|
||||
|
||||
|
||||
def _only_the_gated_exporter(config: "OpenTelemetryV2Config") -> "OpenTelemetryV2Config":
|
||||
return config.model_copy(
|
||||
update={"exporters": [spec for spec in config.exporters if _is_gated(spec)]} # mutable-ok: model_copy update
|
||||
|
|
|
|||
|
|
@ -1174,7 +1174,7 @@ class TestPresetDegradation:
|
|||
"https://otlp.nr-data.net",
|
||||
]
|
||||
|
||||
def test_a_credentialless_newrelic_without_a_base_exporter_falls_back(self, monkeypatch, capsys):
|
||||
def test_a_credentialless_newrelic_without_a_base_exporter_falls_back(self, monkeypatch):
|
||||
from litellm.litellm_core_utils.litellm_logging import _maybe_construct_otel_v2
|
||||
|
||||
monkeypatch.delenv("NEW_RELIC_LICENSE_KEY", raising=False)
|
||||
|
|
@ -1187,7 +1187,6 @@ class TestPresetDegradation:
|
|||
is_otel_v2_enabled.cache_clear()
|
||||
|
||||
assert logger is None
|
||||
assert capsys.readouterr().out == ""
|
||||
|
||||
def test_a_destination_for_one_backend_does_not_degrade_another(self, monkeypatch):
|
||||
from litellm.litellm_core_utils.litellm_logging import _maybe_construct_otel_v2
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue