LangChain.OpenTelemetry.Attributes (LangChain v0.9.2)

Copy Markdown View Source

Builds OpenTelemetry span attribute maps from LangChain telemetry metadata, following a subset of the GenAI Semantic Conventions (v1.40+).

Attribute key constants are defined as string literals because the Hex opentelemetry_semantic_conventions package lags behind the latest spec.

Coverage

This integration emits the following semantic-convention attributes:

  • gen_ai.operation.name, gen_ai.provider.name, gen_ai.output.type ("json" when the model requests structured output, else "text")
  • gen_ai.request.model, gen_ai.response.model
  • server.address, server.port (derived from the model's request endpoint)
  • Request parameters (when the model sets them): gen_ai.request.temperature, gen_ai.request.max_tokens, gen_ai.request.top_p, gen_ai.request.top_k, gen_ai.request.frequency_penalty, gen_ai.request.presence_penalty, gen_ai.request.seed, gen_ai.request.choice.count, gen_ai.request.stream, gen_ai.request.stop_sequences, gen_ai.request.reasoning.level
  • gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, and — best-effort from the provider-specific TokenUsage.rawgen_ai.usage.cache_read.input_tokens, gen_ai.usage.cache_creation.input_tokens, gen_ai.usage.reasoning.output_tokens
  • gen_ai.response.finish_reasons (best-effort from Message.status)
  • gen_ai.input.messages, gen_ai.output.messages (opt-in — see Config)
  • gen_ai.tool.name, gen_ai.tool.call.id, gen_ai.tool.type, gen_ai.tool.description, gen_ai.tool.call.arguments / gen_ai.tool.call.result (opt-in)
  • gen_ai.agent.name, gen_ai.conversation.id (from custom_context)
  • error.type (on failed operations)

It does not currently emit gen_ai.response.id or a distinct gen_ai.response.model (LangChain keeps no response id and normalizes the response model to the requested one). Treat the output as a useful subset rather than full conformance.

Streaming time-to-first-token is captured, but by LangChain.OpenTelemetry.SpanHandler (as a gen_ai.response.time_to_first_token attribute and a gen_ai.first_token span event) rather than here, because it is derived from a streaming lifecycle event rather than the call metadata this module maps.

See: https://opentelemetry.io/docs/specs/semconv/gen-ai/

Summary

Functions

Builds attributes for a chain execution event.

Builds attributes for a chain execution stop event.

Extracts Langfuse-specific attributes from a custom_context map.

Builds attributes for an LLM call start event.

Builds attributes for an LLM call stop event (token usage and response model).

Returns the gen_ai.operation.name attribute key.

Builds attributes for a tool call start event.

Builds attributes for a tool call stop event.

Functions

chain_start(metadata)

@spec chain_start(map()) :: [{String.t(), term()}]

Builds attributes for a chain execution event.

Sets gen_ai.conversation.id from custom_context (a :conversation_id key, falling back to :langfuse_session_id — the same session concept) and also extracts Langfuse-specific attributes from custom_context when present.

chain_stop(metadata, config)

@spec chain_stop(map(), LangChain.OpenTelemetry.Config.t()) :: [{String.t(), term()}]

Builds attributes for a chain execution stop event.

Extracts the first user message as input and the last assistant message as output so they appear on the trace-level span in Langfuse (and other OTEL backends).

custom_context_attributes(context)

@spec custom_context_attributes(map() | nil) :: [{String.t(), term()}]

Extracts Langfuse-specific attributes from a custom_context map.

Supported keys:

  • :langfuse_trace_name -> langfuse.trace.name
  • :langfuse_user_id -> langfuse.user.id
  • :langfuse_session_id -> langfuse.session.id
  • :langfuse_tags -> langfuse.trace.tags
  • :langfuse_metadata -> langfuse.trace.metadata.* (flattened)

llm_call_start(metadata, config \\ %Config{})

@spec llm_call_start(map(), LangChain.OpenTelemetry.Config.t()) :: [
  {String.t(), term()}
]

Builds attributes for an LLM call start event.

Returns operation name, output type, model, provider, and request-parameter attributes (gen_ai.request.*, sourced from metadata[:request_options]). Input message capture is handled separately by the prompt event handler in SpanHandler.

llm_call_stop(metadata, config \\ %Config{})

@spec llm_call_stop(map(), LangChain.OpenTelemetry.Config.t()) :: [
  {String.t(), term()}
]

Builds attributes for an LLM call stop event (token usage and response model).

When config.capture_output_messages is true and metadata[:result] contains a message, serializes output messages into gen_ai.output.messages.

operation_name_key()

Returns the gen_ai.operation.name attribute key.

tool_call(metadata, config \\ %Config{})

@spec tool_call(map(), LangChain.OpenTelemetry.Config.t()) :: [{String.t(), term()}]

Builds attributes for a tool call start event.

When config.capture_tool_arguments is true and metadata[:arguments] is present, serializes arguments into gen_ai.tool.call.arguments.

tool_call_stop(metadata, config)

@spec tool_call_stop(map(), LangChain.OpenTelemetry.Config.t()) :: [
  {String.t(), term()}
]

Builds attributes for a tool call stop event.

When config.capture_tool_results is true and metadata[:tool_result] is present, extracts the result content into gen_ai.tool.call.result.