Replies: 4 comments 3 replies
|
You've identified a real architectural challenge with Langfuse's current trace input/output model in distributed systems. By default, trace input/output is copied from the observation at the top level (the "root observation"), which means if your root observation has no input/output, your trace will be empty unless you set it explicitly.(1) The Core ProblemIn distributed agentic systems, the root span is typically created by an orchestrator or API gateway, while the actual LLM operations happen in downstream services that have no access to modify the root span. The documentation acknowledges this limitation exists.(1) Available Workarounds1. Propagate Attributes via OpenTelemetry Baggage For trace-level attributes like However, there's a critical security consideration: OpenTelemetry baggage is propagated across service boundaries and to third-party APIs. Do not include sensitive information (passwords, API keys, personal data, etc.) in baggage when using this approach.(2) 2. Set Trace Input/Output Explicitly For cases where the trace input/output should be different from the root observation, you can use root_span.set_trace_io(
input={"user_question": user_input},
output={"answer": result}
)However, this method is deprecated and only exists for backward compatibility with trace-level LLM-as-a-judge evaluators.(1) 3. Use Langfuse-Specific Span Attributes For OpenTelemetry integrations, you can set trace-level attributes using the with tracer.start_as_current_span("Langfuse Attributes") as span:
span.set_attribute("langfuse.trace.input", json.dumps(input_data))
span.set_attribute("langfuse.trace.output", json.dumps(output_data))The trace-level attributes LimitationsThe fundamental tension you've identified remains: Langfuse's model assumes the service performing LLM operations can access and modify trace-level context. The documentation doesn't provide a clean solution for distributed scenarios where meaningful trace-level attributes are generated by non-root services. For dual-exporting to Tempo/Loki/Prometheus alongside Langfuse, you can configure an OpenTelemetry Collector to export to multiple backends, with Langfuse receiving traces via its This appears to be a known architectural limitation. Consider opening a feature request on GitHub to discuss potential solutions for distributed agentic systems. 📚 Sources: Have another question? Just tag @inkeep. |
|
You've identified a fundamental architectural challenge with Langfuse's current model. The documentation confirms that trace input/output defaults to mirroring the root observation, and the available workarounds assume the service doing the LLM work has access to the trace context.(1) How Langfuse Handles This CurrentlyCost and token aggregation is automatic at the trace level. Langfuse aggregates usage details (tokens, cost) from all child generations within a trace—you don't need to manually propagate these up to the root. Each generation records its own For trace input/output specifically, the challenge you've described is real. The documentation shows you can use with langfuse.start_as_current_observation(as_type="span", name="child-operation") as span:
# Child service can set trace-level input/output
span.set_trace_io(
input={"user_question": user_input},
output={"answer": result}
)However, this requires the child service to explicitly call this method, and it assumes the child knows what the "meaningful" trace-level input/output should be. Cross-Service PropagationFor distributed tracing, you can propagate attributes via HTTP headers using from langfuse import get_client, propagate_attributes
langfuse = get_client()
with langfuse.start_as_current_observation(as_type="span", name="api-request"):
with propagate_attributes(
user_id="user_123",
session_id="session_abc",
as_baggage=True,
):
requests.get("https://service-b.example.com/api")This propagates The GapThe documentation doesn't provide a clean solution for the scenario where:
This appears to be an architectural limitation. The cost/token aggregation works automatically, but trace-level input/output for distributed agentic systems remains a challenge without explicit coordination between services. 📚 Sources: Have another question? Just tag @inkeep. |
|
You're correct that the Python/JS SDK-specific methods like OpenTelemetry Approach for JavaSince you're using the Java OpenTelemetry SDK (1.60.1), you can set trace-level input/output using Langfuse-specific span attributes. Langfuse maps these OTEL span attributes to trace input/output:(1)
For observation-level input/output, Langfuse checks these attributes in priority order:(1)
So from your Java service that performs the LLM operation, you could set: span.setAttribute("langfuse.trace.input", jsonInputString);
span.setAttribute("langfuse.trace.output", jsonOutputString);This should populate the trace-level input/output in Langfuse, even though the span isn't the root span. The Distributed Tracing ChallengeA related scenario was discussed where traces from distributed services need to share trace context. The approach involves checking incoming headers for an existing trace ID and creating spans with that trace context.(2) However, the fundamental limitation remains: if multiple agents each set 📚 Sources:
Have another question? Just tag @inkeep. |
|
When a trace contains multiple LLM calls, each LLM call should be recorded as a separate generation observation within the trace hierarchy.(1) The nesting and parent-child relationships are handled automatically by OpenTelemetry's context propagation.(1) How Multiple LLM Calls WorkEach LLM operation creates its own generation span with its own with langfuse.start_as_current_observation(as_type="span", name="outer-process") as outer_span:
with langfuse.start_as_current_observation(as_type="generation", name="llm-step-1") as gen1:
gen1.update(output="LLM 1 output")
with langfuse.start_as_current_observation(as_type="generation", name="llm-step-2") as gen2:
gen2.update(output="LLM 2 output")The Trace Input/Output Problem with Multiple LLM CallsThe trace-level If multiple spans set The cost and token aggregation across all generations within a trace is handled by Langfuse's analytics layer, but the trace-level input/output fields remain tied to either the root observation or explicit overrides. 📚 Sources: Have another question? Just tag @inkeep. |
Uh oh!
There was an error while loading. Please reload this page.
Describe your question
In a distributed system, traces and spans can come in from different applications. Each of those applications could be interacting as part of the same trace. I've noticed in the trace/session view in the Langfuse UI. I've noticed that the input & output fields of a trace in the Langfuse UI are only populated if the input/output span attributes are populated into the root span of the entire trace.
How is that supposed to work in practice? In a distributed system, one application may begin the trace, but the actual LLM operation may occur in another application where the parent trace/span is propagated. That application will add the appropriate spans to the trace with the proper attributes.
But the data won't surface in the main Traces view in Langfuse because those gen_ai.* attributes don't exist on the root span. And it would be pretty impossible to "copy" up the attributes to the root level, since the application computing the attribute values wouldn't have access to the entire trace tree, since each application is exporting the spans on their own.
Especially in an agentic system - none of the agents (that actually deal with the llm) are generally ever the system that creates the root span.
One way around this would be to start a new trace at each llm operation, but then you would lose the overall visibility of where the llm operation occurred in the overall request flow. We'd still want to export data to Tempo/Loki/Prometheus as well, since Langfuse is really only interested in the trace data - not exemplars/logs/metrics. We'd still need/want a way to correlate any operation in a trace to its metrics/logs, something that those other tools do, and do well.
Any thoughts/suggestions?
I asked Langfuse AI and it didn't have a good answer.
Langfuse Cloud or Self-Hosted?
Self-Hosted
If Self-Hosted
3.172.1 OSS
If Langfuse Cloud
No response
SDK and integration versions
OpenTelemetry 1.60.1 (Java SDK)
Pre-Submission Checklist
All reactions