mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-17 04:34:25 +00:00
fix(langfuse): start generation span under metadata trace-id context
Langfuse derives a trace's identity from the OTEL span-context trace id, but LangfuseOtelLogger only ever wrote metadata.trace_id as a span attribute, so the exported trace kept a random id and metadata.trace_id from extra_body was ignored (#31378). Override _get_span_context to anchor the otherwise-random root span to the normalized trace id derived from existing_trace_id/trace_id, building a non-recording parent context the same way the Langfuse SDK does for trace_context={'trace_id': ...}. Any upstream parent (explicit parent span, traceparent header, active span) still wins, so distributed tracing and existing_trace_id continuation are preserved. Fixes #31378
This commit is contained in:
parent
246dfd1872
commit
9d2063a169
1 changed files with 58 additions and 0 deletions
|
|
@ -18,11 +18,14 @@ from litellm.types.integrations.langfuse_otel import (
|
|||
from litellm.types.utils import StandardCallbackDynamicParams
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from opentelemetry.context import Context as _Context
|
||||
from opentelemetry.trace import Span as _Span
|
||||
|
||||
Span = Union[_Span, Any]
|
||||
Context = Union[_Context, Any]
|
||||
else:
|
||||
Span = Any
|
||||
Context = Any
|
||||
|
||||
|
||||
LANGFUSE_CLOUD_EU_ENDPOINT = "https://cloud.langfuse.com/api/public/otel"
|
||||
|
|
@ -101,6 +104,61 @@ class LangfuseOtelLogger(OpenTelemetry):
|
|||
|
||||
return metadata
|
||||
|
||||
@staticmethod
|
||||
def _effective_langfuse_trace_id(kwargs) -> Optional[str]:
|
||||
"""Return the user-provided Langfuse trace id seed, if any.
|
||||
|
||||
``existing_trace_id`` continues an existing trace and wins over
|
||||
``trace_id``, matching the vanilla Langfuse precedence.
|
||||
"""
|
||||
metadata = LangfuseOtelLogger._extract_langfuse_metadata(kwargs)
|
||||
raw = metadata.get("existing_trace_id") or metadata.get("trace_id")
|
||||
return raw if isinstance(raw, str) and raw else None
|
||||
|
||||
@staticmethod
|
||||
def _trace_id_parent_context(otel_trace_id_hex: str) -> "Context":
|
||||
"""Build an OTEL context whose non-recording parent carries
|
||||
``otel_trace_id_hex`` so a span started under it inherits that trace id.
|
||||
|
||||
Mirrors how the Langfuse SDK turns ``trace_context={"trace_id": ...}``
|
||||
into a context: a non-recording parent with a synthetic span id and the
|
||||
sampled flag set.
|
||||
"""
|
||||
from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
|
||||
from opentelemetry.trace import (
|
||||
NonRecordingSpan,
|
||||
SpanContext,
|
||||
TraceFlags,
|
||||
set_span_in_context,
|
||||
)
|
||||
|
||||
span_context = SpanContext(
|
||||
trace_id=int(otel_trace_id_hex, 16),
|
||||
span_id=RandomIdGenerator().generate_span_id(),
|
||||
is_remote=False,
|
||||
trace_flags=TraceFlags(TraceFlags.SAMPLED),
|
||||
)
|
||||
return set_span_in_context(NonRecordingSpan(span_context))
|
||||
|
||||
def _get_span_context(self, kwargs, default_span: Optional[Span] = None):
|
||||
"""Honor an explicit Langfuse trace id from request metadata.
|
||||
|
||||
Any upstream parent (explicit parent span, traceparent header, or active
|
||||
span) still wins, preserving distributed-tracing behavior. Only the
|
||||
otherwise-random root span is anchored to the normalized trace id so the
|
||||
Langfuse trace identity matches the caller's ``metadata.trace_id``.
|
||||
"""
|
||||
ctx, parent_span = super()._get_span_context(kwargs, default_span=default_span)
|
||||
if ctx is not None or parent_span is not None:
|
||||
return ctx, parent_span
|
||||
|
||||
effective_trace_id = self._effective_langfuse_trace_id(kwargs)
|
||||
if effective_trace_id is None:
|
||||
return ctx, parent_span
|
||||
|
||||
otel_trace_id = normalize_trace_id_for_otel(effective_trace_id)
|
||||
return self._trace_id_parent_context(otel_trace_id), None
|
||||
|
||||
@staticmethod
|
||||
def _set_metadata_attributes(span: Span, metadata: dict):
|
||||
"""Helper to set metadata attributes from mapping."""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue