mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-17 04:34:25 +00:00
fix(langfuse): add deterministic otel trace-id normalization helper
Add normalize_trace_id_for_otel: a valid 32-hex id (case-insensitive) or a dashed UUID is reused directly; any other string is hashed via sha256(id).digest()[:16].hex(), identical to langfuse.create_trace_id(seed), so callers can reproduce the trace id from an external id. Not wired into span creation yet; covered by unit tests.
This commit is contained in:
parent
868c0bbfac
commit
246dfd1872
2 changed files with 76 additions and 0 deletions
|
|
@ -1,7 +1,9 @@
|
|||
import base64
|
||||
import json # <--- NEW
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
from hashlib import sha256
|
||||
from typing import TYPE_CHECKING, Any, Optional, Union
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
|
|
@ -26,6 +28,23 @@ else:
|
|||
LANGFUSE_CLOUD_EU_ENDPOINT = "https://cloud.langfuse.com/api/public/otel"
|
||||
LANGFUSE_CLOUD_US_ENDPOINT = "https://us.cloud.langfuse.com/api/public/otel"
|
||||
|
||||
_OTEL_TRACE_ID_HEX = re.compile(r"[0-9a-f]{32}")
|
||||
|
||||
|
||||
def normalize_trace_id_for_otel(trace_id: str) -> str:
|
||||
"""Map an arbitrary trace id onto a valid 32-hex-char OTEL/Langfuse trace id.
|
||||
|
||||
Langfuse derives a trace's identity from the OTEL span-context trace id, which
|
||||
must be 32 lowercase hex chars. An id that is already valid (case-insensitively,
|
||||
or a dashed UUID) is reused directly so an explicit valid id is preserved; any
|
||||
other string is hashed exactly as ``langfuse.create_trace_id(seed=...)`` does,
|
||||
so a caller can reproduce the trace id from their external id.
|
||||
"""
|
||||
canonical = trace_id.replace("-", "").lower()
|
||||
if _OTEL_TRACE_ID_HEX.fullmatch(canonical):
|
||||
return canonical
|
||||
return sha256(trace_id.encode("utf-8")).digest()[:16].hex()
|
||||
|
||||
|
||||
class LangfuseOtelLogger(OpenTelemetry):
|
||||
def __init__(self, config=None, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -550,6 +550,63 @@ class TestLangfuseOtelIntegration:
|
|||
# Endpoint assertion removed as side effect is gone
|
||||
|
||||
|
||||
class TestNormalizeTraceIdForOtel:
|
||||
"""Unit tests for the trace-id normalization helper."""
|
||||
|
||||
def test_valid_lowercase_hex_passthrough(self):
|
||||
from litellm.integrations.langfuse.langfuse_otel import (
|
||||
normalize_trace_id_for_otel,
|
||||
)
|
||||
|
||||
tid = "abcdef0123456789abcdef0123456789"
|
||||
assert normalize_trace_id_for_otel(tid) == tid
|
||||
|
||||
def test_uppercase_hex_is_lowercased(self):
|
||||
from litellm.integrations.langfuse.langfuse_otel import (
|
||||
normalize_trace_id_for_otel,
|
||||
)
|
||||
|
||||
assert (
|
||||
normalize_trace_id_for_otel("ABCDEF0123456789ABCDEF0123456789")
|
||||
== "abcdef0123456789abcdef0123456789"
|
||||
)
|
||||
|
||||
def test_dashed_uuid_is_stripped_not_hashed(self):
|
||||
from litellm.integrations.langfuse.langfuse_otel import (
|
||||
normalize_trace_id_for_otel,
|
||||
)
|
||||
|
||||
assert (
|
||||
normalize_trace_id_for_otel("123e4567-e89b-12d3-a456-426614174000")
|
||||
== "123e4567e89b12d3a456426614174000"
|
||||
)
|
||||
|
||||
def test_external_id_hashed_like_langfuse_create_trace_id(self):
|
||||
"""Non-hex ids hash the ORIGINAL string (dashes and case preserved) so
|
||||
the result is byte-for-byte ``langfuse.create_trace_id(seed=...)``."""
|
||||
import hashlib
|
||||
|
||||
from litellm.integrations.langfuse.langfuse_otel import (
|
||||
normalize_trace_id_for_otel,
|
||||
)
|
||||
|
||||
for seed in ("global_20260225143025_ord2468", "Order-42", "ab-cd"):
|
||||
assert (
|
||||
normalize_trace_id_for_otel(seed)
|
||||
== hashlib.sha256(seed.encode("utf-8")).digest()[:16].hex()
|
||||
)
|
||||
|
||||
def test_output_is_always_32_lowercase_hex(self):
|
||||
import re
|
||||
|
||||
from litellm.integrations.langfuse.langfuse_otel import (
|
||||
normalize_trace_id_for_otel,
|
||||
)
|
||||
|
||||
for seed in ("x", "global_seed", "ABCDEF0123456789ABCDEF0123456789", "1-2-3"):
|
||||
assert re.fullmatch(r"[0-9a-f]{32}", normalize_trace_id_for_otel(seed))
|
||||
|
||||
|
||||
class TestLangfuseOtelResponsesAPI:
|
||||
"""Test suite for Langfuse OTEL integration with ResponsesAPI"""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue