Skip to content
6 changes: 0 additions & 6 deletions langfuse/_client/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ class LangfuseOtelSpanAttributes:
TRACE_TAGS = "langfuse.trace.tags"
TRACE_PUBLIC = "langfuse.trace.public"
TRACE_METADATA = "langfuse.trace.metadata"
TRACE_INPUT = "langfuse.trace.input"
TRACE_OUTPUT = "langfuse.trace.output"

# Langfuse-observation attributes
OBSERVATION_TYPE = "langfuse.observation.type"
Expand Down Expand Up @@ -76,13 +74,9 @@ class LangfuseOtelSpanAttributes:

def create_trace_attributes(
*,
input: Optional[Any] = None,
output: Optional[Any] = None,
public: Optional[bool] = None,
) -> dict:
attributes = {
LangfuseOtelSpanAttributes.TRACE_INPUT: _serialize(input),
LangfuseOtelSpanAttributes.TRACE_OUTPUT: _serialize(output),
LangfuseOtelSpanAttributes.TRACE_PUBLIC: public,
}

Expand Down
52 changes: 1 addition & 51 deletions langfuse/_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
_agnosticcontextmanager,
)
from packaging.version import Version
from typing_extensions import deprecated

from langfuse._client.attributes import (
LangfuseOtelSpanAttributes,
Expand Down Expand Up @@ -213,7 +212,7 @@ class Langfuse:
release (Optional[str]): Release version/hash of your application. Used for grouping analytics by release.
media_upload_thread_count (Optional[int]): Number of background threads for handling media uploads. Defaults to 1. Can also be set via LANGFUSE_MEDIA_UPLOAD_THREAD_COUNT environment variable.
sample_rate (Optional[float]): Sampling rate for traces (0.0 to 1.0). Defaults to 1.0 (100% of traces are sampled). Can also be set via LANGFUSE_SAMPLE_RATE environment variable.
mask (Optional[MaskFunction]): Function to mask sensitive data synchronously when Langfuse SDK attributes are created. This applies only to data set through Langfuse SDK APIs such as `start_observation()`, `update()`, and `set_trace_io()`.
mask (Optional[MaskFunction]): Function to mask sensitive data synchronously when Langfuse SDK attributes are created. This applies only to data set through Langfuse SDK APIs such as `start_observation()` and `update()`.
mask_otel_spans (Optional[MaskOtelSpansFunction]): Synchronous export-stage hook for masking raw OpenTelemetry span attributes before this Langfuse client sends them to Langfuse. Use this for spans created by third-party OpenTelemetry instrumentations, or when you need to inspect final span attributes after export filtering and Langfuse media handling. It does not modify spans already exported through other OpenTelemetry exporters.

The hook receives one OpenTelemetry export batch. A batch is not guaranteed to contain a complete trace, request, or Langfuse observation tree. The hook usually runs on the OpenTelemetry batch span processor worker thread; during `flush()` and shutdown it may run on the caller thread. Keep it synchronous, deterministic, and fast.
Expand Down Expand Up @@ -1534,55 +1533,6 @@ def update_current_span(
status_message=status_message,
)

@deprecated(
"Trace-level input/output is deprecated. "
"For trace attributes (user_id, session_id, tags, etc.), use propagate_attributes() instead. "
"This method will be removed in a future major version."
)
def set_current_trace_io(
self,
*,
input: Optional[Any] = None,
output: Optional[Any] = None,
) -> None:
"""Set trace-level input and output for the current span's trace.

.. deprecated::
This is a legacy method for backward compatibility with Langfuse platform
features that still rely on trace-level input/output (e.g., legacy LLM-as-a-judge
evaluators). It will be removed in a future major version.

For setting other trace attributes (user_id, session_id, metadata, tags, version),
use :func:`langfuse.propagate_attributes` (top-level import) instead.

Args:
input: Input data to associate with the trace.
output: Output data to associate with the trace.
"""
if not self._tracing_enabled:
langfuse_logger.debug(
"Operation skipped: set_current_trace_io - Tracing is disabled or client is in no-op mode."
)
return

current_otel_span = self._get_current_otel_span()

if current_otel_span is not None and current_otel_span.is_recording():
span_class = self._get_span_class(
self._get_observation_type_from_otel_span(current_otel_span)
)
span = span_class(
otel_span=current_otel_span,
langfuse_client=self,
environment=self._environment,
release=self._release,
)

span.set_trace_io(
input=input,
output=output,
)

def set_current_trace_as_public(self) -> None:
"""Make the current trace publicly accessible via its URL.

Expand Down
48 changes: 0 additions & 48 deletions langfuse/_client/span.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
if TYPE_CHECKING:
from langfuse._client.client import Langfuse

from typing_extensions import deprecated

from langfuse._client.attributes import (
LangfuseOtelSpanAttributes,
Expand Down Expand Up @@ -226,53 +225,6 @@ def end(self, *, end_time: Optional[int] = None) -> "LangfuseObservationWrapper"

return self

@deprecated(
"Trace-level input/output is deprecated. "
"For trace attributes (user_id, session_id, tags, etc.), use propagate_attributes() instead. "
"This method will be removed in a future major version."
)
def set_trace_io(
self,
*,
input: Optional[Any] = None,
output: Optional[Any] = None,
) -> "LangfuseObservationWrapper":
"""Set trace-level input and output for the trace this span belongs to.

.. deprecated::
This is a legacy method for backward compatibility with Langfuse platform
features that still rely on trace-level input/output (e.g., legacy LLM-as-a-judge
evaluators). It will be removed in a future major version.

For setting other trace attributes (user_id, session_id, metadata, tags, version),
use :func:`langfuse.propagate_attributes` (top-level import) instead.

Args:
input: Input data to associate with the trace.
output: Output data to associate with the trace.

Returns:
The span instance for method chaining.
"""
if not self._otel_span.is_recording():
return self

media_processed_input = self._process_media_and_apply_mask(
data=input, field="input", span=self._otel_span
)
media_processed_output = self._process_media_and_apply_mask(
data=output, field="output", span=self._otel_span
)

attributes = create_trace_attributes(
input=media_processed_input,
output=media_processed_output,
)

self._otel_span.set_attributes(attributes)

return self

def set_trace_as_public(self) -> "LangfuseObservationWrapper":
"""Make this trace publicly accessible via its URL.

Expand Down
2 changes: 0 additions & 2 deletions langfuse/_client/span_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

_INPUT_MEDIA_ATTRIBUTE_KEYS = frozenset(
{
LangfuseOtelSpanAttributes.TRACE_INPUT,
LangfuseOtelSpanAttributes.OBSERVATION_INPUT,
"ai.prompt.messages",
"ai.prompt",
Expand All @@ -54,7 +53,6 @@

_OUTPUT_MEDIA_ATTRIBUTE_KEYS = frozenset(
{
LangfuseOtelSpanAttributes.TRACE_OUTPUT,
LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT,
"ai.response.text",
"ai.result.text",
Expand Down
20 changes: 10 additions & 10 deletions tests/e2e/test_core_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,14 +549,14 @@ def test_create_update_current_trace():

trace_name = create_uuid()

# Create initial span with trace properties using propagate_attributes and set_current_trace_io
# Create initial span with trace properties using propagate_attributes
with langfuse.start_as_current_observation(name="test-span-current") as span:
with propagate_attributes(
trace_name=trace_name,
user_id="test",
metadata={"key": "value"},
):
langfuse.set_current_trace_io(input="test_input")
langfuse.update_current_span(input="test_input")
langfuse.set_current_trace_as_public()
# Get trace ID for later reference
trace_id = span.trace_id
Expand Down Expand Up @@ -989,7 +989,7 @@ def test_create_trace_and_generation():
# Create parent span and set trace properties
with langfuse.start_as_current_observation(name=trace_name) as parent_span:
with propagate_attributes(trace_name=trace_name, session_id="test-session-id"):
parent_span.set_trace_io(input={"key": "value"})
parent_span.update(input={"key": "value"})

# Create a generation as child
generation = parent_span.start_observation(
Expand Down Expand Up @@ -1812,23 +1812,23 @@ def test_fetch_traces():
# First trace
with langfuse.start_as_current_observation(name="test1") as span:
with propagate_attributes(trace_name=name, session_id="session-1"):
span.set_trace_io(input={"key": "value"}, output="output-value")
span.update(input={"key": "value"}, output="output-value")
trace_ids.append(span.trace_id)

sleep(1) # Ensure traces have different timestamps

# Second trace
with langfuse.start_as_current_observation(name="test2") as span:
with propagate_attributes(trace_name=name, session_id="session-1"):
span.set_trace_io(input={"key": "value"}, output="output-value")
span.update(input={"key": "value"}, output="output-value")
trace_ids.append(span.trace_id)

sleep(1) # Ensure traces have different timestamps

# Third trace
with langfuse.start_as_current_observation(name="test3") as span:
with propagate_attributes(trace_name=name, session_id="session-1"):
span.set_trace_io(input={"key": "value"}, output="output-value")
span.update(input={"key": "value"}, output="output-value")
trace_ids.append(span.trace_id)

# Ensure data is sent
Expand Down Expand Up @@ -2088,11 +2088,11 @@ def mask_func(data):
# Create a root span with trace properties
with langfuse.start_as_current_observation(name="test-span") as root_span:
with propagate_attributes(trace_name="test_trace"):
root_span.set_trace_io(input={"sensitive": "data"})
root_span.update(input={"sensitive": "data"})
# Get trace ID for later use
trace_id = root_span.trace_id
# Add output to the trace
root_span.set_trace_io(output={"more": "sensitive"})
root_span.update(output={"more": "sensitive"})

# Create a generation as child
gen = root_span.start_observation(
Expand Down Expand Up @@ -2136,11 +2136,11 @@ def mask_func(data):
# Create a root span with trace properties
with langfuse.start_as_current_observation(name="test-span") as root_span:
with propagate_attributes(trace_name="test_trace"):
root_span.set_trace_io(input={"should_raise": "data"})
root_span.update(input={"should_raise": "data"})
# Get trace ID for later use
trace_id = root_span.trace_id
# Add output to the trace
root_span.set_trace_io(output={"should_raise": "sensitive"})
root_span.update(output={"should_raise": "sensitive"})

# Ensure data is sent
langfuse.flush()
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1071,10 +1071,10 @@ def test_media():

media = LangfuseMedia(content_bytes=pdf_bytes, content_type="application/pdf")

@observe()
@observe(capture_input=False, capture_output=False)
def main():
sleep(1)
langfuse.set_current_trace_io(
langfuse.update_current_span(
input={
"context": {
"nested": media,
Expand Down
2 changes: 0 additions & 2 deletions tests/unit/test_mask_otel_spans.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,6 @@ def test_export_stage_media_processes_string_sequence_attributes():
@pytest.mark.parametrize(
("attribute_key", "expected_field"),
[
("langfuse.trace.input", "input"),
("langfuse.observation.input", "input"),
("ai.prompt.messages", "input"),
("gcp.vertex.agent.tool_call_args", "input"),
Expand All @@ -302,7 +301,6 @@ def test_export_stage_media_processes_string_sequence_attributes():
("gen_ai.input.messages", "input"),
("gen_ai.prompt.0.content", "input"),
("llm.input_messages.0.message.content", "input"),
("langfuse.trace.output", "output"),
("langfuse.observation.output", "output"),
("ai.response.toolCalls", "output"),
("gcp.vertex.agent.tool_response", "output"),
Expand Down
8 changes: 3 additions & 5 deletions tests/unit/test_otel.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,16 +498,16 @@ def test_generation_name_update(self, langfuse_client, memory_exporter):

def test_trace_update(self, langfuse_client, memory_exporter):
"""Test updating trace level attributes."""
# Create a span and set trace attributes using propagate_attributes and set_trace_io
with langfuse_client.start_as_current_observation(name="trace-span") as span:
# Create a span and set trace attributes using propagate_attributes
with langfuse_client.start_as_current_observation(name="trace-span"):
with propagate_attributes(
trace_name="updated-trace-name",
user_id="test-user",
session_id="test-session",
tags=["tag1", "tag2"],
metadata={"trace-meta": "data"},
):
span.set_trace_io(input={"trace-input": "value"})
pass

# Get the span data
spans = self.get_spans_by_name(memory_exporter, "trace-span")
Expand All @@ -526,12 +526,10 @@ def test_trace_update(self, langfuse_client, memory_exporter):
else:
tags = list(attributes[LangfuseOtelSpanAttributes.TRACE_TAGS])

input_data = json.loads(attributes[LangfuseOtelSpanAttributes.TRACE_INPUT])
metadata = attributes[f"{LangfuseOtelSpanAttributes.TRACE_METADATA}.trace-meta"]

# Check attribute values
assert sorted(tags) == sorted(["tag1", "tag2"])
assert input_data == {"trace-input": "value"}
assert metadata == "data"

def test_complex_scenario(self, langfuse_client, memory_exporter):
Expand Down
Loading