Implementation:Langfuse Langfuse ObservationTypeMapper
| Knowledge Sources | |
|---|---|
| Domains | Observability, OpenTelemetry, AI Frameworks |
| Last Updated | 2026-02-14 00:00 GMT |
Overview
Concrete tool for classifying OpenTelemetry spans into Langfuse observation types via a priority-ordered mapper registry provided by Langfuse.
Description
The ObservationTypeMapperRegistry class maintains an ordered list of ObservationTypeMapper instances, each with a priority number. When mapToObservationType() is called, the registry iterates through mappers sorted by priority (ascending), calling canMap() then mapToObservationType() on each. The first mapper that returns a non-null result wins. If none match, the default is SPAN.
Two mapper base classes are provided:
- SimpleAttributeMapper: Checks for a single attribute key and maps its value through a static dictionary. Used for straightforward 1:1 mappings (P1: Langfuse direct, P2: OpenInference, P3: GenAI operation).
- CustomAttributeMapper: Accepts arbitrary
canMapFnandmapFnclosures for complex multi-attribute logic. Used for conditional detection (P0: Python SDK override, P4/P5: Vercel AI SDK, P6: GenAI tool, P7: model-based fallback).
The sorted mapper list is cached after first computation for performance.
Usage
The registry is instantiated once as a module-level singleton in OtelIngestionProcessor.ts and used during both processToIngestionEvents() and processToEvent() to classify each span.
Code Reference
Source Location
- Repository: langfuse
- File: packages/shared/src/server/otel/ObservationTypeMapper.ts
- Lines: 152-432 (registry class), 412-432 (main mapToObservationType method)
Signature
class ObservationTypeMapperRegistry {
mapToObservationType(
attributes: Record<string, unknown>,
resourceAttributes?: Record<string, unknown>,
scopeData?: Record<string, unknown>,
): LangfuseObservationType;
getMappersForDebugging(): ReadonlyArray<ObservationTypeMapper>;
}
Import
import { ObservationTypeMapperRegistry } from "@langfuse/shared/src/server/otel/ObservationTypeMapper";
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| attributes | Record<string, unknown> | Yes | Span-level attributes extracted from the OTel span (e.g., langfuse.observation.type, gen_ai.operation.name, openinference.span.kind)
|
| resourceAttributes | Record<string, unknown> | No | Resource-level attributes from the OTel resourceSpan (e.g., telemetry.sdk.language)
|
| scopeData | Record<string, unknown> | No | Instrumentation scope metadata (e.g., { name: "langfuse-sdk", version: "3.5.0" })
|
Outputs
| Name | Type | Description |
|---|---|---|
| LangfuseObservationType | string (enum key) | One of: SPAN, GENERATION, EVENT, EMBEDDING, AGENT, TOOL, CHAIN, RETRIEVER, GUARDRAIL, EVALUATOR. Defaults to SPAN if no mapper matches. |
Usage Examples
Basic Type Detection
const registry = new ObservationTypeMapperRegistry();
// Langfuse SDK span with explicit type
const type1 = registry.mapToObservationType(
{ "langfuse.observation.type": "generation" },
);
// Returns: "GENERATION"
// OpenInference LLM span
const type2 = registry.mapToObservationType(
{ "openinference.span.kind": "LLM" },
);
// Returns: "GENERATION"
// Vercel AI SDK generation with model
const type3 = registry.mapToObservationType(
{
"operation.name": "ai.generateText.doGenerate",
"ai.model.id": "gpt-4o",
},
);
// Returns: "GENERATION"
// Unknown span with model attribute (P7 fallback)
const type4 = registry.mapToObservationType(
{ "gen_ai.request.model": "claude-3-opus" },
);
// Returns: "GENERATION"
// Plain span with no recognized attributes
const type5 = registry.mapToObservationType({});
// Returns: "SPAN"
Python SDK v3.3.0 Override (P0)
const registry = new ObservationTypeMapperRegistry();
// Python SDK <= 3.3.0 incorrectly sets type to "span" for generations
const type = registry.mapToObservationType(
{
"langfuse.observation.type": "span",
"langfuse.observation.model.name": "gpt-4",
"langfuse.observation.usage_details": '{"input": 100, "output": 50}',
},
{ "telemetry.sdk.language": "python" },
{ name: "langfuse-sdk", version: "3.2.0" },
);
// Returns: "GENERATION" (P0 override corrects the misclassification)
GenAI Tool Call Detection (P6)
const registry = new ObservationTypeMapperRegistry();
// Pydantic AI tool call (no gen_ai.operation.name set)
const type = registry.mapToObservationType(
{
"gen_ai.tool.name": "search_database",
"gen_ai.tool.call.id": "call_abc123",
},
);
// Returns: "TOOL"