mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-23 05:34:41 +00:00
fix(langfuse_otel): read string mask flags the way the classic callback does
Some checks failed
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Waiting to run
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled
Some checks failed
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Waiting to run
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled
The langfuse_mask_input / langfuse_mask_output headers arrive as strings, so "false" redacted the derived trace io. Parse them with str_to_bool and fall back to truthiness, as the classic langfuse callback does
This commit is contained in:
parent
63f1722089
commit
1106e25f2e
3 changed files with 32 additions and 2 deletions
|
|
@ -16,6 +16,7 @@ from litellm.integrations.langfuse.langfuse_otel_attributes import (
|
|||
from litellm.integrations.opentelemetry import OpenTelemetry, OpenTelemetryConfig
|
||||
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
|
||||
from litellm.litellm_core_utils.safe_json_loads import safe_json_loads
|
||||
from litellm.secret_managers.main import str_to_bool
|
||||
from litellm.types.integrations.langfuse_otel import (
|
||||
LangfuseSpanAttributes,
|
||||
)
|
||||
|
|
@ -54,6 +55,12 @@ def _preset_cache_key(kwargs: Mapping[str, object]) -> object:
|
|||
return litellm.cache.get_preset_cache_key_from_kwargs(**kwargs)
|
||||
|
||||
|
||||
def _mask_flag(value: object) -> bool:
|
||||
"""A mask flag as the classic langfuse callback reads it: "true" / "false" strings parse, anything else by truthiness."""
|
||||
parsed: Final = str_to_bool(value) if isinstance(value, str) else None
|
||||
return bool(value) if parsed is None else parsed
|
||||
|
||||
|
||||
def _caller_tag_list(value: object) -> tuple[str, ...]:
|
||||
"""Caller tags as Langfuse ingestion reads a raw tags value: a list, a JSON array string, a comma list or one tag."""
|
||||
parsed: Final = (
|
||||
|
|
@ -248,7 +255,7 @@ class LangfuseOtelLogger(OpenTelemetry):
|
|||
safe_set_attribute(
|
||||
span,
|
||||
LangfuseSpanAttributes.TRACE_INPUT.value,
|
||||
REDACTED_BY_LITELLM if metadata.get("mask_input") else input_json,
|
||||
REDACTED_BY_LITELLM if _mask_flag(metadata.get("mask_input")) else input_json,
|
||||
)
|
||||
|
||||
output_json: Final = LangfuseOtelLogger._observation_output(response_obj)
|
||||
|
|
@ -258,7 +265,7 @@ class LangfuseOtelLogger(OpenTelemetry):
|
|||
safe_set_attribute(
|
||||
span,
|
||||
LangfuseSpanAttributes.TRACE_OUTPUT.value,
|
||||
REDACTED_BY_LITELLM if metadata.get("mask_output") else output_json,
|
||||
REDACTED_BY_LITELLM if _mask_flag(metadata.get("mask_output")) else output_json,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -337,6 +337,26 @@ def test_mask_flags_redact_the_derived_trace_io(v1: Rig) -> None:
|
|||
assert attributes.get("langfuse.trace.name") == "litellm-acompletion", attributes
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("headers", "masked"),
|
||||
[
|
||||
pytest.param({"langfuse_mask_input": "true", "langfuse_mask_output": "true"}, True, id="true"),
|
||||
pytest.param({"langfuse_mask_input": "false", "langfuse_mask_output": "false"}, False, id="false"),
|
||||
],
|
||||
)
|
||||
def test_mask_flag_headers_parse_as_booleans(v1: Rig, headers: Mapping[str, str], masked: bool) -> None:
|
||||
marker: Final = "lit8281-mask-header-" + uuid.uuid4().hex
|
||||
_chat(v1, marker, headers=headers)
|
||||
attributes: Final = v1.sink.one(marker).attributes
|
||||
expected: Final = (
|
||||
(REDACTED, REDACTED)
|
||||
if masked
|
||||
else (attributes.get("langfuse.observation.input"), attributes.get("langfuse.observation.output"))
|
||||
)
|
||||
assert (attributes.get("langfuse.trace.input"), attributes.get("langfuse.trace.output")) == expected, attributes
|
||||
assert marker in str(attributes.get("langfuse.observation.input")), attributes
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tags",
|
||||
[
|
||||
|
|
|
|||
|
|
@ -1127,6 +1127,9 @@ class TestDerivedTraceFields:
|
|||
({"mask_input": True}, {"langfuse.trace.input"}),
|
||||
({"mask_output": True}, {"langfuse.trace.output"}),
|
||||
({"mask_input": True, "mask_output": True}, {"langfuse.trace.input", "langfuse.trace.output"}),
|
||||
({"mask_input": "true", "mask_output": "TRUE"}, {"langfuse.trace.input", "langfuse.trace.output"}),
|
||||
({"mask_input": "false", "mask_output": "False"}, set()),
|
||||
({"mask_input": False, "mask_output": None}, set()),
|
||||
],
|
||||
)
|
||||
def test_mask_flags_redact_the_derived_trace_io(self, mask_flags, masked_trace_keys):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue