OTEL-based Python SDK

The latest generation of the Langfuse Python SDK, built on the OpenTelemetry framework.
On Day 5 of our Launch Week #3, we’re introducing the Langfuse Python SDK v3 (OpenTelemetry-based) in beta.
This is a significant update to our Python SDK as it is now built on the OpenTelemetry (OTEL) standard and designed to improve developer experience and enhance integration capabilities for tracing your LLM applications.
Key Improvement: OpenTelemetry Integration
The foundation of the v3 SDK is OpenTelemetry, which brings several practical advantages:
- Standardized Context Propagation: OTEL automatically handles the propagation of trace and span context. This means when you create a new span or generation, it correctly nests under the currently active operation.
- Third-Party Library Compatibility: Libraries already instrumented with OpenTelemetry will integrate with the Langfuse SDK, with their spans being captured and correctly nested within your Langfuse traces.
Simplified Tracing and Global Client Access
The new SDK aims to make tracing more straightforward. Here’s an example of how tracing can be implemented across different modules, leveraging automatic context propagation and global client access:
# main_app.py
from langfuse import get_client, observe
import other_module
# If no client is initialized, it will be initialized with the default configuration
# from the environment variables
langfuse = get_client()
@observe(name="user-request-pipeline")
def handle_user_request(user_query: str, user_id: str):
# This span is now active. Enrich the trace with user info.
langfuse.update_current_trace(user_id=user_id, tags=["experimental"])
# Call a function from another module
processed_data = other_module.process_data(user_query)
# Update the root span
langfuse.update_current_span(output={"final_result": processed_data})
return processed_data
handle_user_request("Tell me a joke about OpenTelemetry", "user_123")
# other_module.py
from langfuse import get_client, observe
# Access the initialized Langfuse client globally
# If no client is initialized, it will be initialized with the default configuration
# from the environment variables
langfuse_client = get_client()
@observe(name="data-processing-step")
def process_data(query: str):
# This span is automatically a child of "user-request-pipeline".
with langfuse_client.start_as_current_generation(
name="joke-generation-llm",
model="gpt-4o",
input=[{"role": "user", "content": query}]
) as generation:
# Simulate LLM call
joke = "Why did the OpenTelemetry collector break up with the span? It needed more space for its attributes!"
generation.update(output=joke, usage_details={"input_tokens": 10, "output_tokens": 25})
return joke
In this example:
Langfuse
is initialized once.- The
@observe
decorator creates spans forhandle_user_request
andprocess_data
. other_module.py
accesses the same client instance viaget_client()
.- Spans are automatically nested due to OTEL’s context propagation.
- Trace and span updates can be done contextually.
Seamless third-party integrations
The Langfuse SDK seamlessly integrates with any third-party library that uses OpenTelemetry instrumentation. When these libraries emit spans, they are automatically captured and properly nested within your trace hierarchy. This enables unified tracing across your entire application stack without requiring any additional configuration.
For example, if you’re using OpenTelemetry-instrumented databases, HTTP clients, or other services alongside your LLM operations, all these spans will be correctly organized within your traces in Langfuse.
You can use any third-party, OTEL-based instrumentation library for Anthropic to automatically trace all your Anthropic API calls in Langfuse.
In this example, we are using the opentelemetry-instrumentation-anthropic
library.
from anthropic import Anthropic
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
from langfuse import get_client
# This will automatically emit OTEL-spans for all Anthropic API calls
AnthropicInstrumentor().instrument()
langfuse = get_client()
anthropic_client = Anthropic()
with langfuse.start_as_current_span(name="myspan"):
# This will be traced as a Langfuse generation nested under the current span
message = anthropic_client.messages.create(
model="claude-3-7-sonnet-20250219",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(message.content)
# Flush events to Langfuse in short-lived applications
langfuse.flush()
Key Changes and Improvements:
- Context Management: OpenTelemetry now handles context propagation automatically, reducing the need to manually pass IDs.
- API for Observations:
- Traces are implicitly created by the first (root) span or generation.
- Use
langfuse.start_as_current_span()
orlangfuse.start_as_current_generation()
with context managers for automatic lifecycle management. - Manual creation via
langfuse.start_span()
orlangfuse.start_generation()
requires an explicit.end()
call. - The
name
parameter is now required when creating spans and generations.
- Observation IDs: Trace and Observation (Span) IDs adhere to the W3C Trace Context format. Use
langfuse.create_trace_id()
for generating IDs, including deterministic ones using aseed
. - Updating Observations:
- Use the
.update()
method onLangfuseSpan
orLangfuseGeneration
objects. - Update the currently active observation via
langfuse.update_current_span()
orlangfuse.update_current_generation()
. - Update trace-level attributes via
span_obj.update_trace()
orlangfuse.update_current_trace()
.
- Use the
- Integrations (OpenAI, Langchain): Trace-level attributes (like
user_id
,session_id
) are now managed by creating an enclosing Langfuse span around the integrated library calls. - LlamaIndex: There is no longer a Langfuse-specific LlamaIndex integration; use third-party OTEL-based instrumentations.
We encourage you to try the new Python SDK v3 beta and welcome your feedback on GitHub.
Learn more