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.integrations.opentelemetry import OpenTelemetry, OpenTelemetryConfig
|
||||||
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
|
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.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 (
|
from litellm.types.integrations.langfuse_otel import (
|
||||||
LangfuseSpanAttributes,
|
LangfuseSpanAttributes,
|
||||||
)
|
)
|
||||||
|
|
@ -54,6 +55,12 @@ def _preset_cache_key(kwargs: Mapping[str, object]) -> object:
|
||||||
return litellm.cache.get_preset_cache_key_from_kwargs(**kwargs)
|
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, ...]:
|
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."""
|
"""Caller tags as Langfuse ingestion reads a raw tags value: a list, a JSON array string, a comma list or one tag."""
|
||||||
parsed: Final = (
|
parsed: Final = (
|
||||||
|
|
@ -248,7 +255,7 @@ class LangfuseOtelLogger(OpenTelemetry):
|
||||||
safe_set_attribute(
|
safe_set_attribute(
|
||||||
span,
|
span,
|
||||||
LangfuseSpanAttributes.TRACE_INPUT.value,
|
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)
|
output_json: Final = LangfuseOtelLogger._observation_output(response_obj)
|
||||||
|
|
@ -258,7 +265,7 @@ class LangfuseOtelLogger(OpenTelemetry):
|
||||||
safe_set_attribute(
|
safe_set_attribute(
|
||||||
span,
|
span,
|
||||||
LangfuseSpanAttributes.TRACE_OUTPUT.value,
|
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
|
@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
|
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(
|
@pytest.mark.parametrize(
|
||||||
"tags",
|
"tags",
|
||||||
[
|
[
|
||||||
|
|
|
||||||
|
|
@ -1127,6 +1127,9 @@ class TestDerivedTraceFields:
|
||||||
({"mask_input": True}, {"langfuse.trace.input"}),
|
({"mask_input": True}, {"langfuse.trace.input"}),
|
||||||
({"mask_output": True}, {"langfuse.trace.output"}),
|
({"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": "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):
|
def test_mask_flags_redact_the_derived_trace_io(self, mask_flags, masked_trace_keys):
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue