Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ requires-python = ">=3.10,<4"
dependencies = [
"opentelemetry-api>=1.38.0,<2",
"opentelemetry-instrumentation>=0.59b0",
"opentelemetry-semantic-conventions-ai>=0.4.11",
"opentelemetry-semantic-conventions-ai>=0.4.11,<0.5.0",
"opentelemetry-semantic-conventions>=0.59b0",
]

Expand Down
189 changes: 189 additions & 0 deletions packages/opentelemetry-semantic-conventions-ai/MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# Migration Guide: opentelemetry-semantic-conventions-ai v0.4.x → v0.5.x

This guide covers breaking changes introduced when aligning the `opentelemetry-semantic-conventions-ai`
package with the upstream [OTel GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/).

---

## 1. Removed constants (previously duplicated upstream)

These `SpanAttributes` constants have been **removed**. They are now part of the official
`opentelemetry-semantic-conventions` package. Import them directly from upstream.

```python
# Before
from opentelemetry.semconv_ai import SpanAttributes
span.set_attribute(SpanAttributes.LLM_SYSTEM, "openai")

# After
from opentelemetry.semconv._incubating.attributes import gen_ai_attributes as GenAIAttributes
span.set_attribute(GenAIAttributes.GEN_AI_SYSTEM, "openai")
```

| Removed constant | Upstream replacement |
|---|---|
| `SpanAttributes.LLM_SYSTEM` | `GenAIAttributes.GEN_AI_SYSTEM` |
| `SpanAttributes.LLM_REQUEST_MODEL` | `GenAIAttributes.GEN_AI_REQUEST_MODEL` |
| `SpanAttributes.LLM_REQUEST_MAX_TOKENS` | `GenAIAttributes.GEN_AI_REQUEST_MAX_TOKENS` |
| `SpanAttributes.LLM_REQUEST_TEMPERATURE` | `GenAIAttributes.GEN_AI_REQUEST_TEMPERATURE` |
| `SpanAttributes.LLM_REQUEST_TOP_P` | `GenAIAttributes.GEN_AI_REQUEST_TOP_P` |
| `SpanAttributes.LLM_TOP_K` | `GenAIAttributes.GEN_AI_REQUEST_TOP_K` |
| `SpanAttributes.LLM_CHAT_STOP_SEQUENCES` | `GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES` |
| `SpanAttributes.LLM_FREQUENCY_PENALTY` | `GenAIAttributes.GEN_AI_REQUEST_FREQUENCY_PENALTY` |
| `SpanAttributes.LLM_PRESENCE_PENALTY` | `GenAIAttributes.GEN_AI_REQUEST_PRESENCE_PENALTY` |
| `SpanAttributes.LLM_RESPONSE_MODEL` | `GenAIAttributes.GEN_AI_RESPONSE_MODEL` |
| `SpanAttributes.LLM_USAGE_COMPLETION_TOKENS` | `GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS` |
| `SpanAttributes.LLM_USAGE_PROMPT_TOKENS` | `GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS` |
| `SpanAttributes.LLM_TOKEN_TYPE` | `GenAIAttributes.GEN_AI_TOKEN_TYPE` |
| `SpanAttributes.LLM_REQUEST_FUNCTIONS` | `GenAIAttributes.GEN_AI_TOOL_DEFINITIONS` |
| `SpanAttributes.LLM_PROMPTS` | `GenAIAttributes.GEN_AI_PROMPT` |
| `SpanAttributes.LLM_COMPLETIONS` | `GenAIAttributes.GEN_AI_COMPLETION` |
| `SpanAttributes.LLM_OPENAI_RESPONSE_SYSTEM_FINGERPRINT` | `GenAIAttributes.GEN_AI_OPENAI_RESPONSE_SYSTEM_FINGERPRINT` |
| `SpanAttributes.LLM_REQUEST_TYPE` | `GenAIAttributes.GEN_AI_OPERATION_NAME` |

> **Note on `LLM_REQUEST_TYPE`**: The old `LLMRequestTypeValues` enum is replaced by
> `GenAiOperationNameValues` from upstream, or by `GenAICustomOperationName` for
> project-specific operation names.

---

## 2. Renamed constants (stay in `SpanAttributes`, new `GEN_AI_*` prefix)

These constants remain in the `opentelemetry-semantic-conventions-ai` package but their
Python names have been renamed from `LLM_*` to `GEN_AI_*`.

```python
# Before
from opentelemetry.semconv_ai import SpanAttributes
span.set_attribute(SpanAttributes.LLM_IS_STREAMING, True)

# After
from opentelemetry.semconv_ai import SpanAttributes
span.set_attribute(SpanAttributes.GEN_AI_IS_STREAMING, True)
```

| Old name | New name |
|---|---|
| `SpanAttributes.LLM_USAGE_TOTAL_TOKENS` | `SpanAttributes.GEN_AI_USAGE_TOTAL_TOKENS` |
| `SpanAttributes.LLM_USER` | `SpanAttributes.GEN_AI_USER` |
| `SpanAttributes.LLM_HEADERS` | `SpanAttributes.GEN_AI_HEADERS` |
| `SpanAttributes.LLM_IS_STREAMING` | `SpanAttributes.GEN_AI_IS_STREAMING` |
| `SpanAttributes.LLM_REQUEST_REPETITION_PENALTY` | `SpanAttributes.GEN_AI_REQUEST_REPETITION_PENALTY` |
| `SpanAttributes.LLM_REQUEST_REASONING_EFFORT` | `SpanAttributes.GEN_AI_REQUEST_REASONING_EFFORT` |
| `SpanAttributes.LLM_REQUEST_REASONING_SUMMARY` | `SpanAttributes.GEN_AI_REQUEST_REASONING_SUMMARY` |
| `SpanAttributes.LLM_RESPONSE_REASONING_EFFORT` | `SpanAttributes.GEN_AI_RESPONSE_REASONING_EFFORT` |
| `SpanAttributes.LLM_RESPONSE_FINISH_REASON` | `SpanAttributes.GEN_AI_RESPONSE_FINISH_REASON` |
| `SpanAttributes.LLM_RESPONSE_STOP_REASON` | `SpanAttributes.GEN_AI_RESPONSE_STOP_REASON` |
| `SpanAttributes.LLM_CONTENT_COMPLETION_CHUNK` | `SpanAttributes.GEN_AI_CONTENT_COMPLETION_CHUNK` |
| `SpanAttributes.LLM_USAGE_REASONING_TOKENS` | `SpanAttributes.GEN_AI_USAGE_REASONING_TOKENS` |
| `SpanAttributes.LLM_USAGE_TOKEN_TYPE` | `SpanAttributes.GEN_AI_USAGE_TOKEN_TYPE` |
| `SpanAttributes.LLM_USAGE_CACHE_CREATION_INPUT_TOKENS` | `SpanAttributes.GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS` ¹ |
| `SpanAttributes.LLM_USAGE_CACHE_READ_INPUT_TOKENS` | `SpanAttributes.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS` ¹ |
| `SpanAttributes.LLM_REQUEST_STRUCTURED_OUTPUT_SCHEMA` | `SpanAttributes.GEN_AI_REQUEST_STRUCTURED_OUTPUT_SCHEMA` |
| `SpanAttributes.LLM_OPENAI_API_BASE` | `SpanAttributes.GEN_AI_OPENAI_API_BASE` |
| `SpanAttributes.LLM_OPENAI_API_VERSION` | `SpanAttributes.GEN_AI_OPENAI_API_VERSION` |
| `SpanAttributes.LLM_OPENAI_API_TYPE` | `SpanAttributes.GEN_AI_OPENAI_API_TYPE` |
| `SpanAttributes.LLM_DECODING_METHOD` | `SpanAttributes.GEN_AI_WATSONX_DECODING_METHOD` |
| `SpanAttributes.LLM_RANDOM_SEED` | `SpanAttributes.GEN_AI_WATSONX_RANDOM_SEED` |
| `SpanAttributes.LLM_MAX_NEW_TOKENS` | `SpanAttributes.GEN_AI_WATSONX_MAX_NEW_TOKENS` |
| `SpanAttributes.LLM_MIN_NEW_TOKENS` | `SpanAttributes.GEN_AI_WATSONX_MIN_NEW_TOKENS` |
| `SpanAttributes.LLM_REPETITION_PENALTY` | `SpanAttributes.GEN_AI_WATSONX_REPETITION_PENALTY` |

> ¹ The string value of these two cache-token attributes **also changed** — see [section 3](#cache-token-attributes).

---

## 3. Changed string values

Some constants kept their Python name but the underlying **string value** changed.

### Cache token attributes

| Python name | Old string value | New string value |
|---|---|---|
| `SpanAttributes.GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS` | `gen_ai.usage.cache_creation_input_tokens` | `gen_ai.usage.cache_creation.input_tokens` |
| `SpanAttributes.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS` | `gen_ai.usage.cache_read_input_tokens` | `gen_ai.usage.cache_read.input_tokens` |

> **Dashboard impact**: Update any Grafana queries, alerts, or OTLP processors that filter on
> these attribute names.

### `GenAISystem` values

All `GenAISystem` enum values now use the OTel spec canonical form (lowercase / dot-separated).

| Enum member | Old value | New value |
|---|---|---|
| `GenAISystem.ANTHROPIC` | `"Anthropic"` | `"anthropic"` |
| `GenAISystem.COHERE` | `"Cohere"` | `"cohere"` |
| `GenAISystem.MISTRALAI` | `"MistralAI"` | `"mistral_ai"` |
| `GenAISystem.OLLAMA` | `"Ollama"` | `"ollama"` |
| `GenAISystem.GROQ` | `"Groq"` | `"groq"` |
| `GenAISystem.ALEPH_ALPHA` | `"AlephAlpha"` | `"aleph_alpha"` |
| `GenAISystem.REPLICATE` | `"Replicate"` | `"replicate"` |
| `GenAISystem.TOGETHER_AI` | `"TogetherAI"` | `"together_ai"` |
| `GenAISystem.WATSONX` | `"Watsonx"` | `"ibm.watsonx.ai"` |
| `GenAISystem.HUGGINGFACE` | `"HuggingFace"` | `"hugging_face"` |
| `GenAISystem.FIREWORKS` | `"Fireworks"` | `"fireworks"` |
| `GenAISystem.AZURE` | `"Azure"` | `"az.ai.openai"` |
| `GenAISystem.AWS` | `"AWS"` | `"aws.bedrock"` |
| `GenAISystem.GOOGLE` | `"Google"` | `"gcp.gen_ai"` |
| `GenAISystem.OPENROUTER` | `"OpenRouter"` | `"openrouter"` |
| `GenAISystem.LANGCHAIN` | `"Langchain"` | `"langchain"` |

> `GenAISystem.OPENAI` (`"openai"`) is unchanged.

> **Dashboard impact**: Update dashboards, alerts, and OTLP processors that filter on
> `gen_ai.system` to use the new lowercase values shown above.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

---

## 4. Tool definitions format change

Tool definitions are now encoded as a **single JSON-array attribute** instead of per-field
indexed sub-attributes.

```python
# Before — multiple flat attributes
span.set_attribute("gen_ai.tool.definitions.0.name", "my_tool")
span.set_attribute("gen_ai.tool.definitions.0.description", "Does something")
span.set_attribute("gen_ai.tool.definitions.0.parameters", json.dumps({...}))

# After — one JSON array attribute
import json
tool_defs = [
{
"name": "my_tool",
"description": "Does something",
"parameters": {...},
}
]
span.set_attribute(GenAIAttributes.GEN_AI_TOOL_DEFINITIONS, json.dumps(tool_defs))
```
Comment on lines +152 to +161
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Migration snippet is incomplete: GenAIAttributes is referenced without import.

The “After” example will fail when copied as-is because GenAIAttributes is undefined.

Suggested doc fix
 # After — one JSON array attribute
 import json
+from opentelemetry.semconv._incubating.attributes import gen_ai_attributes as GenAIAttributes
 tool_defs = [
     {
         "name": "my_tool",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opentelemetry-semantic-conventions-ai/MIGRATION.md` around lines 152
- 161, The snippet uses GenAIAttributes (and GEN_AI_TOOL_DEFINITIONS in
span.set_attribute) but never imports it; add an import for GenAIAttributes
before the example (e.g., import GenAIAttributes from the package that exports
it such as opentelemetry_semantic_conventions_ai) so the After example runs
as-is and the span.set_attribute(GenAIAttributes.GEN_AI_TOOL_DEFINITIONS, ...)
reference resolves.


> **Dashboard impact**: Dashboards that expand `gen_ai.tool.definitions.{i}.name` as individual
> attributes will no longer find them. Parse the JSON value of `gen_ai.tool.definitions` instead.

---

## 5. Quickstart: minimal import update

```python
# Before
from opentelemetry.semconv_ai import SpanAttributes

SpanAttributes.LLM_SYSTEM # removed
SpanAttributes.LLM_REQUEST_MODEL # removed
SpanAttributes.LLM_REQUEST_TYPE # removed
SpanAttributes.LLM_IS_STREAMING # renamed
SpanAttributes.LLM_USAGE_TOTAL_TOKENS # renamed

# After
from opentelemetry.semconv_ai import SpanAttributes
from opentelemetry.semconv._incubating.attributes import gen_ai_attributes as GenAIAttributes

GenAIAttributes.GEN_AI_SYSTEM # upstream
GenAIAttributes.GEN_AI_REQUEST_MODEL # upstream
GenAIAttributes.GEN_AI_OPERATION_NAME # upstream
SpanAttributes.GEN_AI_IS_STREAMING # project semconv (renamed)
SpanAttributes.GEN_AI_USAGE_TOTAL_TOKENS # project semconv (renamed)
```
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,31 @@ class GenAISystem(Enum):
"""
Supported LLM vendor (System) names used across OpenLLMetry instrumentations.

These values match the actual strings used in span attributes (LLM_SYSTEM)
throughout the instrumentation packages.
Values that have a counterpart in the official OTel GenAI semantic conventions
(opentelemetry.semconv._incubating.attributes.gen_ai_attributes.GenAiSystemValues)
use the spec-defined lowercase string. Values without an OTel counterpart use
lowercase-with-underscores as a project convention.
"""

OPENAI = "openai"
ANTHROPIC = "Anthropic"
COHERE = "Cohere"
MISTRALAI = "MistralAI"
OLLAMA = "Ollama"
GROQ = "Groq"
ALEPH_ALPHA = "AlephAlpha"
REPLICATE = "Replicate"
TOGETHER_AI = "TogetherAI"
WATSONX = "Watsonx"
HUGGINGFACE = "HuggingFace"
FIREWORKS = "Fireworks"

AZURE = "Azure"
AWS = "AWS"
GOOGLE = "Google"
OPENROUTER = "OpenRouter"

LANGCHAIN = "Langchain"
ANTHROPIC = "anthropic"
COHERE = "cohere"
MISTRALAI = "mistral_ai"
OLLAMA = "ollama"
GROQ = "groq"
ALEPH_ALPHA = "aleph_alpha"
REPLICATE = "replicate"
TOGETHER_AI = "together_ai"
WATSONX = "ibm.watsonx.ai"
HUGGINGFACE = "hugging_face"
FIREWORKS = "fireworks"

AZURE = "az.ai.openai"
AWS = "aws.bedrock"
GOOGLE = "gcp.gen_ai"
OPENROUTER = "openrouter"

LANGCHAIN = "langchain"
CREWAI = "crewai"


Expand Down Expand Up @@ -62,52 +64,32 @@ class Meters:


class SpanAttributes:
# GenAI Usage Cache Attributes (missing from incubating semantic conventions)
GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = "gen_ai.usage.cache_creation_input_tokens"
GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read_input_tokens"

# LLM Cache Attributes (legacy naming - keeping for backward compatibility)
LLM_SYSTEM = "gen_ai.system"
LLM_REQUEST_MODEL = "gen_ai.request.model"
LLM_REQUEST_MAX_TOKENS = "gen_ai.request.max_tokens"
LLM_REQUEST_TEMPERATURE = "gen_ai.request.temperature"
LLM_REQUEST_TOP_P = "gen_ai.request.top_p"
LLM_PROMPTS = "gen_ai.prompt"
LLM_COMPLETIONS = "gen_ai.completion"
LLM_RESPONSE_MODEL = "gen_ai.response.model"
LLM_USAGE_COMPLETION_TOKENS = "gen_ai.usage.completion_tokens"
LLM_USAGE_PROMPT_TOKENS = "gen_ai.usage.prompt_tokens"
LLM_USAGE_CACHE_CREATION_INPUT_TOKENS = "gen_ai.usage.cache_creation_input_tokens"
LLM_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read_input_tokens"
LLM_TOKEN_TYPE = "gen_ai.token.type"
LLM_REQUEST_STRUCTURED_OUTPUT_SCHEMA = "gen_ai.request.structured_output_schema"
LLM_REQUEST_REASONING_SUMMARY = "gen_ai.request.reasoning_summary"
LLM_RESPONSE_REASONING_EFFORT = "gen_ai.response.reasoning_effort"

# LLM
LLM_REQUEST_TYPE = "llm.request.type"
LLM_USAGE_TOTAL_TOKENS = "llm.usage.total_tokens"
LLM_USAGE_TOKEN_TYPE = "llm.usage.token_type"
LLM_USER = "llm.user"
LLM_HEADERS = "llm.headers"
LLM_TOP_K = "llm.top_k"
LLM_IS_STREAMING = "llm.is_streaming"
LLM_FREQUENCY_PENALTY = "llm.frequency_penalty"
LLM_PRESENCE_PENALTY = "llm.presence_penalty"
LLM_CHAT_STOP_SEQUENCES = "llm.chat.stop_sequences"
LLM_REQUEST_FUNCTIONS = "llm.request.functions"
LLM_REQUEST_REPETITION_PENALTY = "llm.request.repetition_penalty"
LLM_RESPONSE_FINISH_REASON = "llm.response.finish_reason"
LLM_RESPONSE_STOP_REASON = "llm.response.stop_reason"
LLM_CONTENT_COMPLETION_CHUNK = "llm.content.completion.chunk"
LLM_REQUEST_REASONING_EFFORT = "llm.request.reasoning_effort"
LLM_USAGE_REASONING_TOKENS = "llm.usage.reasoning_tokens"
# GenAI Usage Cache Attributes (not yet in upstream OTel incubating semconv)
GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = "gen_ai.usage.cache_creation.input_tokens"
GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens"

# LLM — project-policy attributes (not in upstream OTel spec)
GEN_AI_USAGE_TOTAL_TOKENS = "gen_ai.usage.total_tokens"
GEN_AI_USAGE_TOKEN_TYPE = "gen_ai.usage.token_type"
GEN_AI_USER = "gen_ai.user"
GEN_AI_HEADERS = "gen_ai.headers"
GEN_AI_IS_STREAMING = "gen_ai.is_streaming"
GEN_AI_REQUEST_REPETITION_PENALTY = "gen_ai.request.repetition_penalty"
GEN_AI_RESPONSE_FINISH_REASON = "gen_ai.response.finish_reason"
GEN_AI_RESPONSE_STOP_REASON = "gen_ai.response.stop_reason"
GEN_AI_CONTENT_COMPLETION_CHUNK = "gen_ai.content.completion.chunk"
GEN_AI_REQUEST_REASONING_EFFORT = "gen_ai.request.reasoning_effort"
GEN_AI_USAGE_REASONING_TOKENS = "gen_ai.usage.reasoning_tokens"
GEN_AI_REQUEST_N = "gen_ai.request.n"
GEN_AI_REQUEST_MAX_COMPLETION_TOKENS = "gen_ai.request.max_completion_tokens"
GEN_AI_REQUEST_STRUCTURED_OUTPUT_SCHEMA = "gen_ai.request.structured_output_schema"
GEN_AI_REQUEST_REASONING_SUMMARY = "gen_ai.request.reasoning_summary"
GEN_AI_RESPONSE_REASONING_EFFORT = "gen_ai.response.reasoning_effort"

# OpenAI
LLM_OPENAI_RESPONSE_SYSTEM_FINGERPRINT = "gen_ai.openai.system_fingerprint"
LLM_OPENAI_API_BASE = "gen_ai.openai.api_base"
LLM_OPENAI_API_VERSION = "gen_ai.openai.api_version"
LLM_OPENAI_API_TYPE = "gen_ai.openai.api_type"
GEN_AI_OPENAI_API_BASE = "gen_ai.openai.api_base"
GEN_AI_OPENAI_API_VERSION = "gen_ai.openai.api_version"
GEN_AI_OPENAI_API_TYPE = "gen_ai.openai.api_type"

# Haystack
HAYSTACK_OPENAI_CHAT = "haystack.openai.chat"
Expand Down Expand Up @@ -152,11 +134,11 @@ class SpanAttributes:
TRACELOOP_CORRELATION_ID = "traceloop.correlation.id"

# Watson/genai LLM
LLM_DECODING_METHOD = "llm.watsonx.decoding_method"
LLM_RANDOM_SEED = "llm.watsonx.random_seed"
LLM_MAX_NEW_TOKENS = "llm.watsonx.max_new_tokens"
LLM_MIN_NEW_TOKENS = "llm.watsonx.min_new_tokens"
LLM_REPETITION_PENALTY = "llm.watsonx.repetition_penalty"
GEN_AI_WATSONX_DECODING_METHOD = "llm.watsonx.decoding_method"
GEN_AI_WATSONX_RANDOM_SEED = "llm.watsonx.random_seed"
GEN_AI_WATSONX_MAX_NEW_TOKENS = "llm.watsonx.max_new_tokens"
GEN_AI_WATSONX_MIN_NEW_TOKENS = "llm.watsonx.min_new_tokens"
GEN_AI_WATSONX_REPETITION_PENALTY = "llm.watsonx.repetition_penalty"
Comment thread
max-deygin-traceloop marked this conversation as resolved.

# Chroma db
CHROMADB_ADD_IDS_COUNT = "db.chroma.add.ids_count"
Expand Down
Loading
Loading