> ## Documentation Index
> Fetch the complete documentation index at: https://futureagi.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Enriching Spans with Attributes, Metadata, and Tags

> When building applications, you'll often need to capture additional context beyond what standard frameworks or LLM clients provide. Here's how to enrich your traces with custom information.

## Add attributes to a span

Attributes are key/value pairs that provide more information about the operation being traced. They help paint a complete picture of what's happening in your application.

To avoid naming conflicts with [semantic conventions](/future-agi/get-started/observability/manual-tracing/set-session-user-id), it's recommended to prefix your custom attributes with your company name (e.g., mycompany.).

<CodeGroup>
  ```python Python theme={null}
  from opentelemetry import trace

  current_span = trace.get_current_span()

  current_span.set_attribute("operation.value", 1)
  current_span.set_attribute("operation.name", "Saying hello!")
  current_span.set_attribute("operation.other-stuff", [1, 2])
  ```

  ```typescript JS/TS theme={null}
  import { trace, context } from "@opentelemetry/api";

  const currentSpan = trace.getSpan(context.active());

  if (currentSpan) {
      currentSpan.setAttribute("mycompany.operation.value", 1);
      currentSpan.setAttribute("mycompany.operation.name", "Saying hello!");
      currentSpan.setAttribute("mycompany.operation.other-stuff", [1, 2]);
  }
  ```
</CodeGroup>

## Leveraging Semantic Convention Attributes

Semantic Conventions provides a structured schema to represent common LLM application attributes. These are well known names for items like messages, prompt templates, metadata, and more. We've built a set of semantic conventions as part of the traceAI package.

Defining attributes is vital for comprehending the data and message flow within your LLM application and helps in debugging and analysis. By defining attributes like `OUTPUT_VALUE` and `OUTPUT_MESSAGES`, you can capture essential output information and interaction messages within a span's context. This enables you to log the response and systematically categorize and store messages exchanged by components.

To use traceAI Semantic Attributes, ensure you have the appropriate FI Instrumentation Package installed:

<CodeGroup>
  ```bash Python theme={null}
  pip install fi-instrumentation-otel
  ```

  ```bash JS/TS theme={null}
  npm install @traceai/fi-core @opentelemetry/api
  ```
</CodeGroup>

Then run the following to set semantic attributes:

<CodeGroup>
  ```python Python theme={null}
  from opentelemetry import trace # Assuming span is current_span or obtained otherwise
  from fi_instrumentation.fi_types import SpanAttributes, MessageAttributes # Assuming these constants and 'response' are defined

  span = trace.get_current_span() # Example: get current span

  if span.is_recording(): # Check if span is recording before setting attributes
      span.set_attribute(SpanAttributes.OUTPUT_VALUE, response)

      # This shows up under `output_messages` tab on the span page
      span.set_attribute(
          f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_ROLE}",
          "user",
      )
      span.set_attribute(
          f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_CONTENT}",
          response,
      )
  ```

  ```typescript JS/TS theme={null}
  import { trace, context } from "@opentelemetry/api";

  // Assume 'response' variable is defined, e.g.:
  // const response: string = "Some LLM response from Typescript";
  // String keys below should match traceAI's expected semantic conventions for Typescript.

  const span = trace.getSpan(context.active());

  if (span) {
      span.setAttribute("output.value", response);
      span.setAttribute("llm_output_messages.0.message_role", "user");
      span.setAttribute("llm_output_messages.0.message_content", response);
  }
  ```
</CodeGroup>

## Adding attributes to multiple spans at once

Our tracing system allows you to set attributes at the OpenTelemetry Context level, which automatically propagates to child spans within a parent trace. In OpenTelemetry, this is often achieved using Baggage. Attributes set in Baggage can be picked up by instrumentation (like traceAI's auto-instrumentation) and added to spans.

Key Context Attributes include:

* **Metadata**: Metadata associated with a span.
* **Tags**: List of tags to give the span a category.
* **Session ID**: Unique identifier for a session.
* **User ID**: Unique identifier for a user.
* **Prompt Template**:
  * Template: Used to generate prompts as Python f-strings.
  * Version: The version of the prompt template.
  * Variables: key-value pairs applied to the prompt template.

Below are examples showing how to manage these attributes. The Python examples use helpers from `fi_instrumentation`. The Typescript examples use standard OpenTelemetry JS API (`context` and `propagation` for Baggage).

## `using_metadata`

This context manager enriches the current OpenTelemetry Context with metadata. Our auto-instrumentators will apply this metadata as span attributes following traceAI [semantic conventions](/future-agi/get-started/observability/manual-tracing/set-session-user-id). The metadata must be provided as a string-keyed dictionary, which will be JSON-serialized in the context.

<CodeGroup>
  ```python Python theme={null}
  from fi_instrumentation import using_metadata
  # Assuming value_1, value_2 are defined
  # value_1 = "some data"; value_2 = 123
  metadata = {
      "key-1": value_1,
      "key-2": value_2,
  }
  with using_metadata(metadata):
      # Calls within this block will generate spans with the attributes:
      # "metadata" = "{"key-1": value_1, "key-2": value_2, ... }" # JSON serialized
      pass # Your code here
  ```

  ```typescript JS/TS theme={null}
  import { context, propagation } from "@opentelemetry/api";

  // Assuming value_1, value_2 are defined
  // const value_1 = "some_data"; const value_2 = 42;
  const metadata = {
      "key-1": value_1,
      "key-2": value_2,
  };

  const previousContext = context.active();
  const newBaggage = propagation.createBaggage({
      "metadata": { value: JSON.stringify(metadata) }
  });
  const newContextWithMetadata = propagation.setBaggage(previousContext, newBaggage);

  context.with(newContextWithMetadata, () => {
      // Your code here. Spans created by traceAI auto-instrumentation inside this block
      // should pick up the 'metadata' attribute from baggage.
      // e.g., myInstrumentedFunction();
  });
  ```
</CodeGroup>

It can also be used as a decorator:

```python Python theme={null}
from fi_instrumentation import using_metadata
# Assuming metadata is defined as above
@using_metadata(metadata)
def call_fn(*args, **kwargs):
    # Calls within this function will generate spans with the attributes:
    # "metadata" = "{"key-1": value_1, "key-2": value_2, ... }" # JSON serialized
    pass # Your function code here
```

## `using_tags`

Enhance spans with categorical information using this context manager. It adds tags to the OpenTelemetry Context, which our auto-instrumentators will apply following traceAI conventions. Tags must be provided as a list of strings.

<CodeGroup>
  ```python Python theme={null}
  from fi_instrumentation import using_tags
  # Assuming tags list is defined
  # tags = ["tag_1", "tag_2"]
  with using_tags(tags):
      # Calls within this block will generate spans with the attributes:
      # "tag.tags" = "["tag_1","tag_2",...]"
      pass # Your code here
  ```

  ```typescript JS/TS theme={null}
  import { context, propagation } from "@opentelemetry/api";

  // Assuming tags list is defined, e.g.:
  // const tags = ["tag_A", "tag_B"];

  const previousContext = context.active();
  const newBaggage = propagation.createBaggage({
      "tag.tags": { value: JSON.stringify(tags) } // Stored as JSON string
  });
  const newContextWithTags = propagation.setBaggage(previousContext, newBaggage);

  context.with(newContextWithTags, () => {
      // Your code here. Spans created by traceAI auto-instrumentation inside this block
      // should pick up the 'tag.tags' attribute from baggage.
      // e.g., myInstrumentedFunction();
  });
  ```
</CodeGroup>

It can also be used as a decorator:

```python theme={null}
from fi_instrumentation import using_tags
# Assuming tags is defined as above
@using_tags(tags)
def call_fn(*args, **kwargs):
    # Calls within this function will generate spans with the attributes:
    # "tag.tags" = "["tag_1","tag_2",...]"
    pass # Your function code here
```

## `using_session`

Set a session identifier for all spans within the context. This is useful for grouping related operations under a common session.

<CodeGroup>
  ```python Python theme={null}
  from fi_instrumentation import using_session
  # Assuming session_id is defined
  # session_id = "session_123"
  with using_session(session_id):
      # Calls within this block will generate spans with the attributes:
      # "session.id" = "session_123"
      pass # Your code here
  ```

  ```typescript JS/TS theme={null}
  import { context, propagation } from "@opentelemetry/api";

  // Assuming session_id is defined, e.g.:
  // const session_id = "session_123";

  const previousContext = context.active();
  const newBaggage = propagation.createBaggage({
      "session.id": { value: session_id }
  });
  const newContextWithSession = propagation.setBaggage(previousContext, newBaggage);

  context.with(newContextWithSession, () => {
      // Your code here. Spans created by traceAI auto-instrumentation inside this block
      // should pick up the 'session.id' attribute from baggage.
      // e.g., myInstrumentedFunction();
  });
  ```
</CodeGroup>

It can also be used as a decorator:

```python Python theme={null}
from fi_instrumentation import using_session
# Assuming session_id is defined as above
@using_session(session_id)
def call_fn(*args, **kwargs):
    # Calls within this function will generate spans with the attributes:
    # "session.id" = "session_123"
    pass # Your function code here
```

## `using_user`

Set a user identifier for all spans within the context. This helps in tracking operations performed by specific users.

<CodeGroup>
  ```python Python theme={null}
  from fi_instrumentation import using_user
  # Assuming user_id is defined
  # user_id = "user_456"
  with using_user(user_id):
      # Calls within this block will generate spans with the attributes:
      # "user.id" = "user_456"
      pass # Your code here
  ```

  ```typescript JS/TS theme={null}
  import { context, propagation } from "@opentelemetry/api";

  // Assuming user_id is defined, e.g.:
  // const user_id = "user_456";

  const previousContext = context.active();
  const newBaggage = propagation.createBaggage({
      "user.id": { value: user_id }
  });
  const newContextWithUser = propagation.setBaggage(previousContext, newBaggage);

  context.with(newContextWithUser, () => {
      // Your code here. Spans created by traceAI auto-instrumentation inside this block
      // should pick up the 'user.id' attribute from baggage.
      // e.g., myInstrumentedFunction();
  });
  ```
</CodeGroup>

It can also be used as a decorator:

```python Python theme={null}
from fi_instrumentation import using_user
# Assuming user_id is defined as above
@using_user(user_id)
def call_fn(*args, **kwargs):
    # Calls within this function will generate spans with the attributes:
    # "user.id" = "user_456"
    pass # Your function code here
```

## `using_prompt_template`

This context manager is used to enrich spans with prompt template information. It's particularly useful when you want to track how prompts are constructed and which variables are used.

<CodeGroup>
  ```python Python theme={null}
  from fi_instrumentation import using_prompt_template
  # Assuming template, version, and variables are defined
  # template = "Hello {name}, your age is {age}"
  # version = "v1.0"
  # variables = {"name": "Alice", "age": 30}
  with using_prompt_template(
      template=template,
      version=version,
      variables=variables
  ):
      # Calls within this block will generate spans with the attributes:
      # "llm.prompt_template.template" = "Hello {name}, your age is {age}"
      # "llm.prompt_template.version" = "v1.0"
      # "llm.prompt_template.variables" = '{"name": "Alice", "age": 30}'
      pass # Your code here
  ```

  ```typescript JS/TS theme={null}
  import { context, propagation } from "@opentelemetry/api";

  // Assuming template, version, and variables are defined, e.g.:
  // const template = "Hello {name}, your age is {age}";
  // const version = "v1.0";
  // const variables = {"name": "Alice", "age": 30};

  const previousContext = context.active();
  const newBaggage = propagation.createBaggage({
      "llm.prompt_template.template": { value: template },
      "llm.prompt_template.version": { value: version },
      "llm.prompt_template.variables": { value: JSON.stringify(variables) }
  });
  const newContextWithPromptTemplate = propagation.setBaggage(previousContext, newBaggage);

  context.with(newContextWithPromptTemplate, () => {
      // Your code here. Spans created by traceAI auto-instrumentation inside this block
      // should pick up the prompt template attributes from baggage.
      // e.g., myInstrumentedFunction();
  });
  ```
</CodeGroup>

It can also be used as a decorator:

```python Python theme={null}
from fi_instrumentation import using_prompt_template
# Assuming template, version, and variables are defined as above
@using_prompt_template(
    template=template,
    version=version,
    variables=variables
)
def call_fn(*args, **kwargs):
    # Calls within this function will generate spans with the attributes:
    # "llm.prompt_template.template" = "Hello {name}, your age is {age}"
    # "llm.prompt_template.version" = "v1.0"
    # "llm.prompt_template.variables" = '{"name": "Alice", "age": 30}'
    pass # Your function code here
```

## Combining Multiple Context Managers

You can combine multiple context managers to set various attributes simultaneously:

<CodeGroup>
  ```python Python theme={null}
  from fi_instrumentation import using_metadata, using_tags, using_session, using_user

  metadata = {"experiment": "A/B test", "version": "2.1"}
  tags = ["production", "critical"]
  session_id = "session_789"
  user_id = "user_101"

  with using_metadata(metadata), \
       using_tags(tags), \
       using_session(session_id), \
       using_user(user_id):
      # All spans created within this block will have:
      # - metadata attributes
      # - tag attributes  
      # - session.id attribute
      # - user.id attribute
      pass # Your code here
  ```

  ```typescript JS/TS theme={null}
  import { context, propagation } from "@opentelemetry/api";

  const metadata = {"experiment": "A/B test", "version": "2.1"};
  const tags = ["production", "critical"];
  const session_id = "session_789";
  const user_id = "user_101";

  const previousContext = context.active();
  const newBaggage = propagation.createBaggage({
      "metadata": { value: JSON.stringify(metadata) },
      "tag.tags": { value: JSON.stringify(tags) },
      "session.id": { value: session_id },
      "user.id": { value: user_id }
  });
  const newContextWithAllAttributes = propagation.setBaggage(previousContext, newBaggage);

  context.with(newContextWithAllAttributes, () => {
      // All spans created within this block will have:
      // - metadata attributes
      // - tag attributes  
      // - session.id attribute
      // - user.id attribute
      // e.g., myInstrumentedFunction();
  });
  ```
</CodeGroup>
