Implementation:Langfuse Langfuse ExtractInputAndOutput
| Knowledge Sources | |
|---|---|
| Domains | Observability, OpenTelemetry, AI Frameworks, Data Normalization |
| Last Updated | 2026-02-14 00:00 GMT |
Overview
Concrete tool for extracting and normalizing LLM input/output from OTel span data across 14+ AI framework conventions provided by Langfuse.
Description
The extractInputAndOutput method is a private instance method on the OtelIngestionProcessor class. It implements a waterfall detection pattern that tries each supported AI framework's attribute convention in priority order, returning the first successful extraction.
The method operates on three data sources from an OTel span:
- events: The span events array (used by OTel GenAI semantic conventions and legacy Semantic Kernel).
- attributes: The span attributes dictionary (used by most frameworks).
- instrumentationScopeName: The name of the instrumentation scope (used to identify Vercel AI SDK spans where
scope.name == "ai").
It also accepts an optional domain parameter ("trace" or "observation") that controls whether to prefer trace-level or observation-level Langfuse SDK attributes.
The method produces three outputs:
- input: The extracted prompt/request data (any type -- string, object, array).
- output: The extracted completion/response data (any type).
- filteredAttributes: A sanitized copy of the input attributes with all known input/output keys removed, suitable for inclusion in metadata without duplication.
Usage
This method is called internally during both processToIngestionEvents() (for creating trace and observation events) and processToEvent() (for creating event records). It is called twice per span during ingestion event processing -- once for the trace event (with domain: "trace" for root spans) and once for the observation event.
Code Reference
Source Location
- Repository: langfuse
- File: packages/shared/src/server/otel/OtelIngestionProcessor.ts
- Lines: 1153-1561
Signature
private extractInputAndOutput(params: {
events: any[];
attributes: Record<string, unknown>;
instrumentationScopeName: string;
domain?: "trace" | "observation";
}): {
input: any;
output: any;
filteredAttributes: Record<string, unknown>;
}
Import
// This is a private method on OtelIngestionProcessor and cannot be imported directly.
// It is accessed internally via:
import { OtelIngestionProcessor } from "@langfuse/shared/src/server";
// Then called as: this.extractInputAndOutput({ events, attributes, instrumentationScopeName })
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| events | any[] | Yes | OTel span events array. Used for GenAI semantic convention events (gen_ai.system.message, gen_ai.choice, etc.) and legacy Semantic Kernel events. |
| attributes | Record<string, unknown> | Yes | OTel span attributes dictionary. Contains framework-specific input/output keys. |
| instrumentationScopeName | string | Yes | The instrumentation scope name (e.g., "ai" for Vercel AI SDK, "litellm" for LiteLLM, "langfuse-sdk" for Langfuse). |
| domain | "trace" or "observation" | No | When "trace", prefers trace-level Langfuse attributes (langfuse.trace.input/output). Defaults to observation-level extraction. |
Outputs
| Name | Type | Description |
|---|---|---|
| input | any (null, string, object, array) | Extracted input/prompt data. null if no input was found from any framework. |
| output | any (null, string, object, array) | Extracted output/completion data. null if no output was found from any framework. |
| filteredAttributes | Record<string, unknown> | Copy of input attributes with all known input/output keys removed. Non-string values are JSON-stringified for backward compatibility with metadata storage. |
Usage Examples
Langfuse SDK Extraction
// Called internally during span processing
const result = this.extractInputAndOutput({
events: [],
attributes: {
"langfuse.observation.input": '{"messages": [{"role": "user", "content": "Hello"}]}',
"langfuse.observation.output": '{"role": "assistant", "content": "Hi there!"}',
"langfuse.observation.model.name": "gpt-4o",
},
instrumentationScopeName: "langfuse-sdk",
});
// result.input = '{"messages": [{"role": "user", "content": "Hello"}]}'
// result.output = '{"role": "assistant", "content": "Hi there!"}'
// result.filteredAttributes = { "langfuse.observation.model.name": "gpt-4o" }
Vercel AI SDK Extraction
const result = this.extractInputAndOutput({
events: [],
attributes: {
"ai.prompt.messages": '[{"role":"user","content":"Tell me a joke"}]',
"ai.response.text": "Why did the chicken cross the road?",
"ai.response.toolCalls": '[{"name":"search","args":"{}"}]',
"ai.model.id": "gpt-4o",
},
instrumentationScopeName: "ai",
});
// result.input = '[{"role":"user","content":"Tell me a joke"}]'
// result.output = '{"role":"assistant","content":"Why did the chicken cross the road?","tool_calls":"[{\"name\":\"search\",\"args\":\"{}\"}]"}'
// (text + toolCalls combined into assistant message)
OTel GenAI Events Extraction
const result = this.extractInputAndOutput({
events: [
{
name: "gen_ai.system.message",
attributes: [
{ key: "content", value: { stringValue: "You are helpful." } },
],
},
{
name: "gen_ai.user.message",
attributes: [
{ key: "content", value: { stringValue: "What is 2+2?" } },
],
},
{
name: "gen_ai.choice",
attributes: [
{ key: "content", value: { stringValue: "4" } },
{ key: "index", value: { intValue: 0 } },
],
},
],
attributes: {},
instrumentationScopeName: "openai",
});
// result.input = [
// { role: "system", content: "You are helpful." },
// { role: "user", content: "What is 2+2?" },
// ]
// result.output = { content: "4", index: 0 }
OpenInference Flattened Path Extraction
const result = this.extractInputAndOutput({
events: [],
attributes: {
"llm.input_messages.0.message.role": "user",
"llm.input_messages.0.message.content": "Hello",
"llm.output_messages.0.message.role": "assistant",
"llm.output_messages.0.message.content": "Hi there!",
},
instrumentationScopeName: "openinference",
});
// result.input = [{ message: { role: "user", content: "Hello" } }]
// result.output = [{ message: { role: "assistant", content: "Hi there!" } }]