fix(langfuse_otel): name trace metadata identity keys after their request metadata fields

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yucheng 2026-09-23 07:23:52 +00:00
parent 81a799ec2e
commit 1201677f17
3 changed files with 24 additions and 25 deletions

View file

@ -2,7 +2,6 @@ import base64
import json
import os
from datetime import datetime
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, Final, Optional
from litellm._logging import verbose_logger
@ -30,14 +29,12 @@ LANGFUSE_CLOUD_US_ENDPOINT: Final = "https://us.cloud.langfuse.com/api/public/ot
LANGFUSE_INGESTION_VERSION_HEADER: Final = "x-langfuse-ingestion-version"
LANGFUSE_INGESTION_VERSION: Final = "4"
_TRACE_IDENTITY_FIELDS: Final = MappingProxyType(
{
"user_api_key_alias": "key_alias",
"user_api_key_user_id": "user_id",
"user_api_key_end_user_id": "end_user_id",
"user_api_key_team_id": "team_id",
"user_api_key_team_alias": "team_alias",
}
_TRACE_IDENTITY_FIELDS: Final = (
"user_api_key_alias",
"user_api_key_user_id",
"user_api_key_end_user_id",
"user_api_key_team_id",
"user_api_key_team_alias",
)
@ -152,10 +149,10 @@ class LangfuseOtelLogger(OpenTelemetry):
observation_metadata: Final = log_requester_metadata(redact_user_api_key_info(metadata=request_metadata))
safe_set_attribute(span, LangfuseSpanAttributes.OBSERVATION_METADATA.value, safe_dumps(observation_metadata))
trace_prefix: Final = LangfuseSpanAttributes.TRACE_METADATA.value
for source_key, target_key in _TRACE_IDENTITY_FIELDS.items():
value = observation_metadata.get(source_key)
for field in _TRACE_IDENTITY_FIELDS:
value = observation_metadata.get(field)
if value is not None:
safe_set_attribute(span, f"{trace_prefix}.{target_key}", value)
safe_set_attribute(span, f"{trace_prefix}.{field}", value)
@staticmethod
def _set_observation_output(span: Span, response_obj):

View file

@ -31,6 +31,8 @@ def _langfuse_rig(
marker: str,
) -> Iterator[tuple[Gateway, Wire, Wire]]:
def upstream(request: Request) -> Reply:
if request.method == "GET":
return Reply(body=b'{"data":[]}')
assert request.target.endswith("/chat/completions"), request.target
return Reply(
body=json.dumps(
@ -138,16 +140,16 @@ def test_langfuse_otel_emits_request_metadata_under_langfuse_observation_and_tra
assert {
attribute: attrs.get(attribute)
for attribute in (
"langfuse.trace.metadata.key_alias",
"langfuse.trace.metadata.team_id",
"langfuse.trace.metadata.team_alias",
"langfuse.trace.metadata.end_user_id",
"langfuse.trace.metadata.user_api_key_alias",
"langfuse.trace.metadata.user_api_key_team_id",
"langfuse.trace.metadata.user_api_key_team_alias",
"langfuse.trace.metadata.user_api_key_end_user_id",
)
} == {
"langfuse.trace.metadata.key_alias": key_alias,
"langfuse.trace.metadata.team_id": team_id,
"langfuse.trace.metadata.team_alias": team_alias,
"langfuse.trace.metadata.end_user_id": end_user,
"langfuse.trace.metadata.user_api_key_alias": key_alias,
"langfuse.trace.metadata.user_api_key_team_id": team_id,
"langfuse.trace.metadata.user_api_key_team_alias": team_alias,
"langfuse.trace.metadata.user_api_key_end_user_id": end_user,
}, attrs

View file

@ -512,11 +512,11 @@ class TestLangfuseOtelIntegration:
assert {
key: value for key, value in actual.items() if key.startswith("langfuse.trace.metadata.")
} == {
"langfuse.trace.metadata.key_alias": "prod-key",
"langfuse.trace.metadata.user_id": "user-1",
"langfuse.trace.metadata.end_user_id": "end-user-1",
"langfuse.trace.metadata.team_id": "team-1",
"langfuse.trace.metadata.team_alias": "team-a",
"langfuse.trace.metadata.user_api_key_alias": "prod-key",
"langfuse.trace.metadata.user_api_key_user_id": "user-1",
"langfuse.trace.metadata.user_api_key_end_user_id": "end-user-1",
"langfuse.trace.metadata.user_api_key_team_id": "team-1",
"langfuse.trace.metadata.user_api_key_team_alias": "team-a",
}
def test_request_metadata_redaction_matches_vanilla_langfuse(self, monkeypatch):