mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
* feat(logger): add shared Rust diagnostics and Python logging bridge * feat(logger): dispatch diagnostic processing through Rust * chore: regenerate Cargo.lock after rebase Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: allowlist bounded logging tree walkers in recursive detector Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * perf(logger): skip decoding plain access arguments * test(logger): skip embedded-python logger test when litellm deps are absent Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * style: cargo fmt Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: expect NativeDiagnosticProcessor in the native public surface Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(stub): export NativeDiagnosticProcessor via __new__ in _native.pyi Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor(tracing): rename logger crate and document host sink contract * test(logger): cover exc, stack, and nested extras in the diagnostic filter Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(logger): keep rendered redacted line when template scan flags a key pattern The blanket REDACTED for a changed msg/color template discarded lines whose rendered form was already redacted by the same pipeline, e.g. 'password=%s' became 'REDACTED' instead of 'password=REDACTED'. Only fall back to REDACTED when the rendered form did not change either, which is where interpolation can mangle the key pattern the scrub would otherwise see. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * ci(rust): install python deps so the logger bridge test runs The end-to-end bridge test skipped silently when litellm's Python deps were absent. uv sync --no-install-project installs them without a maturin build, and PYTHONPATH makes them visible to the embedded interpreter Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Yujong Lee <yujong@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
59 lines
2 KiB
Python
59 lines
2 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Sequence
|
|
from functools import lru_cache
|
|
from typing import Final, Protocol, TypeVar, cast
|
|
|
|
from litellm.rust_bridge.bindings import NativeBinding
|
|
|
|
ResultT: Final = TypeVar("ResultT")
|
|
|
|
|
|
class NativeDiagnosticProcessor(Protocol):
|
|
def redact_text(self, text: str) -> str: ...
|
|
def redact_structured_text(self, key: str | None, text: str) -> str: ...
|
|
def redact_client_message(self, text: str) -> str: ...
|
|
def process_diagnostic(
|
|
self,
|
|
message: str,
|
|
exception: str | None,
|
|
stack: str | None,
|
|
leaves: tuple[tuple[str | None, str], ...],
|
|
policy: tuple[bool, int, int],
|
|
) -> tuple[str, str | None, str | None, Sequence[str], bool]: ...
|
|
def scrub_access_arguments(self, arguments: tuple[str, ...]) -> Sequence[str]: ...
|
|
|
|
|
|
class NativeDiagnosticFactory(Protocol):
|
|
def __call__(self, minimum_custom_key_length: int) -> NativeDiagnosticProcessor: ...
|
|
|
|
|
|
def _as_factory(value: object) -> NativeDiagnosticFactory | None:
|
|
if not isinstance(value, type):
|
|
return None
|
|
return cast(NativeDiagnosticFactory, value) # cast-ok: PyO3 factory must be a type
|
|
|
|
|
|
PROCESSOR: Final = NativeBinding("NativeDiagnosticProcessor", validate=_as_factory)
|
|
|
|
|
|
@lru_cache(maxsize=4)
|
|
def _construct(factory: NativeDiagnosticFactory, minimum_custom_key_length: int) -> NativeDiagnosticProcessor:
|
|
return factory(minimum_custom_key_length)
|
|
|
|
|
|
def run(native: Callable[[NativeDiagnosticProcessor], ResultT], python: Callable[[], ResultT]) -> ResultT:
|
|
from litellm.constants import MINIMUM_CUSTOM_KEY_LENGTH
|
|
from litellm.rust_bridge.catalog import LoggerContext, decision
|
|
from litellm.rust_bridge.configuration import Decision
|
|
|
|
selected: Final = decision(LoggerContext())
|
|
if selected is Decision.PYTHON:
|
|
return python()
|
|
factory: Final = PROCESSOR.load()
|
|
if factory is None:
|
|
return python()
|
|
try:
|
|
return native(_construct(factory, MINIMUM_CUSTOM_KEY_LENGTH))
|
|
except Exception:
|
|
return python()
|