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, it’s recommended to prefix your custom attributes with your company name (e.g., mycompany.).

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])

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:

pip install fi-instrumentation-otel

Then run the following to set semantic attributes:

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,
    )

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. The metadata must be provided as a string-keyed dictionary, which will be JSON-serialized in the context.

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

It can also be used as a decorator:

Python
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.

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

It can also be used as a decorator:

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.

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

It can also be used as a decorator:

Python
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.

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

It can also be used as a decorator:

Python
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.

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

It can also be used as a decorator:

Python
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:

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