# `LangChain.OpenTelemetry.Attributes`
[🔗](https://github.com/brainlid/langchain/blob/v0.9.3/lib/open_telemetry/attributes.ex#L1)

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.raw` — `gen_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/

# `chain_start`
[🔗](https://github.com/brainlid/langchain/blob/v0.9.3/lib/open_telemetry/attributes.ex#L422)

```elixir
@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`
[🔗](https://github.com/brainlid/langchain/blob/v0.9.3/lib/open_telemetry/attributes.ex#L457)

```elixir
@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`
[🔗](https://github.com/brainlid/langchain/blob/v0.9.3/lib/open_telemetry/attributes.ex#L524)

```elixir
@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`
[🔗](https://github.com/brainlid/langchain/blob/v0.9.3/lib/open_telemetry/attributes.ex#L104)

```elixir
@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`
[🔗](https://github.com/brainlid/langchain/blob/v0.9.3/lib/open_telemetry/attributes.ex#L192)

```elixir
@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`
[🔗](https://github.com/brainlid/langchain/blob/v0.9.3/lib/open_telemetry/attributes.ex#L92)

Returns the `gen_ai.operation.name` attribute key.

# `tool_call`
[🔗](https://github.com/brainlid/langchain/blob/v0.9.3/lib/open_telemetry/attributes.ex#L347)

```elixir
@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`
[🔗](https://github.com/brainlid/langchain/blob/v0.9.3/lib/open_telemetry/attributes.ex#L397)

```elixir
@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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
