Map langfuse.score.* span attributes to scores on the OTLP ingestion path
#14652
GeorgGroenendaal
started this conversation in
Ideas
Replies: 1 comment
|
Hi @GeorgGroenendaal, great Idea and great suggestion! The more people upvote this Idea, the more likely this is to be added to the Engineer's backlog. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Describe the feature or potential improvement
The OTLP ingestion endpoint (
/api/public/otel) maps a fixed allowlist of spanattributes to the Langfuse data model — trace/observation identity
(
langfuse.trace.*,langfuse.observation.*), IO, usage, and cost. There is nomapping for scores: a span attribute such as
langfuse.score.<name>ridesalong as an unmapped attribute and never materializes as a score
(
OtelIngestionProcessorhas no score handling, andattributes.tsdefines noscore attribute keys).
Today scores must be sent through the dedicated ingestion path (
score-createevents on
/api/public/ingestion), which the official SDKs use internally. Thatis a problem for OTel-only pipelines that have no Langfuse SDK in process
(e.g. agents that export pure OpenTelemetry through a collector). To attach an
inline, code-computed score (a cheap reference-free evaluator running on span
close) we have to make a second, out-of-band HTTP call to the score API,
re-deriving the trace id / observation id from the OTel span context and
re-implementing auth — duplicating transport, batching, and retry logic that the
OTLP exporter already provides.
If Langfuse mapped
langfuse.score.*span attributes to scores during OTLPingestion, an OTel-native producer could attach a score with a single
span.set_attribute(...)and let the existing exporter ship it — no SDK, nosecond sink, no manual id derivation.
Proposed solution
On the OTLP ingestion path, recognize a
langfuse.score.<name>attributenamespace on any span and emit a score attached to the corresponding observation
(and its trace), mirroring how
langfuse.observation.metadata.*is alreadykeyed by suffix.
langfuse.score.<name>→ the score value (number, or string forcategorical).
langfuse.score.<name>.data_type→ optionalNUMERIC|CATEGORICAL|BOOLEAN(inferred from the value type when omitted).langfuse.score.<name>.comment→ optional free-text comment.langfuse.score.<name>.config_id→ optional score config id.The score's
traceId/observationIdare the span's own trace/span ids (thesame derivation OTLP ingestion already does for observations), so the score
lands on the exact span that carried the attributes. Multiple scores per span
are supported by using distinct
<name>segments.Example: how the field could look
Producer side (OpenTelemetry SDK, no Langfuse SDK):
As it arrives in the OTLP payload (span attributes):
{ "attributes": [ { "key": "langfuse.score.correctness", "value": { "doubleValue": 0.92 } }, { "key": "langfuse.score.correctness.data_type", "value": { "stringValue": "NUMERIC" } }, { "key": "langfuse.score.correctness.comment", "value": { "stringValue": "3/3 expected titles present" } }, { "key": "langfuse.score.tone", "value": { "stringValue": "professional" } }, { "key": "langfuse.score.tone.data_type", "value": { "stringValue": "CATEGORICAL" } } ] }Resulting scores (equivalent to the dedicated
score-createevents):[ { "traceId": "<span trace id>", "observationId": "<span id>", "name": "correctness", "value": 0.92, "dataType": "NUMERIC", "comment": "3/3 expected titles present" }, { "traceId": "<span trace id>", "observationId": "<span id>", "name": "tone", "value": "professional", "dataType": "CATEGORICAL" } ]Happy to help with the implementation for this.
Additional information
No response
All reactions