Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ec42339
initial commit
DylanRussell Aug 28, 2025
353243a
lint
DylanRussell Aug 28, 2025
fafa959
Merge branch 'main' into gen_ai_flags
DylanRussell Aug 28, 2025
01cabcd
Fix tests
DylanRussell Aug 28, 2025
45cdaff
Fix typecheck
DylanRussell Aug 28, 2025
b2c5b19
Update opentelemetry-instrumentation/src/opentelemetry/instrumentatio…
DylanRussell Aug 29, 2025
96f2052
Update util/opentelemetry-util-genai/src/opentelemetry/util/genai/uti…
DylanRussell Aug 29, 2025
748f1ca
Use ValueError
DylanRussell Aug 29, 2025
787df1d
Merge branch 'main' into gen_ai_flags
emdneto Aug 29, 2025
185d782
Rename enum
DylanRussell Sep 2, 2025
06bbeb9
Update util/opentelemetry-util-genai/tests/test_utils.py
DylanRussell Sep 3, 2025
80eeea8
Update util/opentelemetry-util-genai/tests/test_utils.py
DylanRussell Sep 3, 2025
338bfd9
Update util/opentelemetry-util-genai/tests/test_utils.py
DylanRussell Sep 3, 2025
695740a
Update util/opentelemetry-util-genai/tests/test_utils.py
DylanRussell Sep 3, 2025
a75d51b
Default env var to NO_CONTENT when invalid envvar
DylanRussell Sep 3, 2025
727dd70
Merge branch 'gen_ai_flags' of github.com:DylanRussell/opentelemetry-…
DylanRussell Sep 3, 2025
6a66251
Address comments
DylanRussell Sep 3, 2025
1b60ec1
Address comment
DylanRussell Sep 3, 2025
1387c6c
Fix typecheck
DylanRussell Sep 3, 2025
a8640cc
don't change typecheck,
DylanRussell Sep 3, 2025
e9adb53
Fix linter
DylanRussell Sep 3, 2025
d6c106e
Merge branch 'main' into gen_ai_flags
DylanRussell Sep 4, 2025
f6aef69
Address comments
DylanRussell Sep 4, 2025
9645a46
Merge branch 'main' into gen_ai_flags
DylanRussell Sep 4, 2025
691eeec
Fix order of args..
DylanRussell Sep 4, 2025
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 @@ -162,9 +162,10 @@
OTEL_SEMCONV_STABILITY_OPT_IN = "OTEL_SEMCONV_STABILITY_OPT_IN"


class _OpenTelemetryStabilitySignalType:
class _OpenTelemetryStabilitySignalType(Enum):
HTTP = "http"
DATABASE = "database"
GEN_AI = "gen_ai"


class _StabilityMode(Enum):
Expand All @@ -173,6 +174,7 @@ class _StabilityMode(Enum):
HTTP_DUP = "http/dup"
DATABASE = "database"
DATABASE_DUP = "database/dup"
GEN_AI_LATEST_EXPERIMENTAL = "gen_ai_latest_experimental"


def _report_new(mode: _StabilityMode):
Expand All @@ -195,14 +197,15 @@ def _initialize(cls):
return

# Users can pass in comma delimited string for opt-in options
# Only values for http and database stability are supported for now
# Only values for http, gen ai, and database stability are supported for now
opt_in = os.environ.get(OTEL_SEMCONV_STABILITY_OPT_IN)

if not opt_in:
# early return in case of default
cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = {
_OpenTelemetryStabilitySignalType.HTTP: _StabilityMode.DEFAULT,
_OpenTelemetryStabilitySignalType.DATABASE: _StabilityMode.DEFAULT,
_OpenTelemetryStabilitySignalType.GEN_AI: _StabilityMode.DEFAULT,
}
cls._initialized = True
return
Expand All @@ -215,14 +218,21 @@ def _initialize(cls):
opt_in_list, _StabilityMode.HTTP, _StabilityMode.HTTP_DUP
)

cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[
_OpenTelemetryStabilitySignalType.GEN_AI
] = cls._filter_mode(
opt_in_list,
_StabilityMode.DEFAULT,
_StabilityMode.GEN_AI_LATEST_EXPERIMENTAL,
)

cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[
_OpenTelemetryStabilitySignalType.DATABASE
] = cls._filter_mode(
opt_in_list,
_StabilityMode.DATABASE,
_StabilityMode.DATABASE_DUP,
)

cls._initialized = True

@staticmethod
Expand Down
23 changes: 22 additions & 1 deletion opentelemetry-instrumentation/tests/test_semconv.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ def test_default_mode(self):
),
_StabilityMode.DEFAULT,
)
self.assertEqual(
_OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.GEN_AI
),
_StabilityMode.DEFAULT,
)

@stability_mode("http")
def test_http_stable_mode(self):
Expand Down Expand Up @@ -91,7 +97,16 @@ def test_database_dup_mode(self):
_StabilityMode.DATABASE_DUP,
)

@stability_mode("database,http")
@stability_mode("gen_ai_latest_experimental")
def test_genai_latest_experimental(self):
self.assertEqual(
_OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.GEN_AI
),
_StabilityMode.GEN_AI_LATEST_EXPERIMENTAL,
)

@stability_mode("database,http,gen_ai_latest_experimental")
def test_multiple_stability_database_http_modes(self):
self.assertEqual(
_OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
Expand All @@ -105,6 +120,12 @@ def test_multiple_stability_database_http_modes(self):
),
_StabilityMode.HTTP,
)
self.assertEqual(
_OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.GEN_AI
),
_StabilityMode.GEN_AI_LATEST_EXPERIMENTAL,
)

@stability_mode("database,http/dup")
def test_multiple_stability_database_http_dup_modes(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = (
"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from enum import Enum


class ContentCapturingMode(Enum):
# Do not capture content (default).
NO_CONTENT = 0
# Only capture content in spans.
SPAN_ONLY = 1
# Only capture content in events.
EVENT_ONLY = 2
# Capture content in both spans and events.
SPAN_AND_EVENT = 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import os

from opentelemetry.instrumentation._semconv import (
_OpenTelemetrySemanticConventionStability,
_OpenTelemetryStabilitySignalType,
_StabilityMode,
)
from opentelemetry.util.genai.environment_variables import (
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT,
)
from opentelemetry.util.genai.types import ContentCapturingMode

logger = logging.getLogger(__name__)


def get_content_capturing_mode() -> ContentCapturingMode:
envvar = os.environ.get(OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT)
if (
_OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.GEN_AI,
)
== _StabilityMode.DEFAULT
):
raise ValueError(
"This function should never be called when StabilityMode is default."
)
if not envvar:
return ContentCapturingMode.NO_CONTENT
try:
return ContentCapturingMode[envvar.upper()]
except KeyError:
logger.warning(
"%s is not a valid option for `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` environment variable. Must be one of %s. Defaulting to `NO_COTENT`.",
envvar,
", ".join(e.name for e in ContentCapturingMode),
)
return ContentCapturingMode.NO_CONTENT
83 changes: 83 additions & 0 deletions util/opentelemetry-util-genai/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import unittest
from unittest.mock import patch

from opentelemetry.instrumentation._semconv import (
OTEL_SEMCONV_STABILITY_OPT_IN,
_OpenTelemetrySemanticConventionStability,
)
from opentelemetry.util.genai.environment_variables import (
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT,
)
from opentelemetry.util.genai.types import ContentCapturingMode
from opentelemetry.util.genai.utils import get_content_capturing_mode


def patch_env_vars(stability_mode, content_capturing):
def decorator(test_case):
@patch.dict(
os.environ,
{
OTEL_SEMCONV_STABILITY_OPT_IN: stability_mode,
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: content_capturing,
},
)
def wrapper(*args, **kwargs):
# Reset state.
_OpenTelemetrySemanticConventionStability._initialized = False
_OpenTelemetrySemanticConventionStability._initialize()
return test_case(*args, **kwargs)

return wrapper

return decorator


class TestVersion(unittest.TestCase):
@patch_env_vars(
stability_mode="gen_ai_latest_experimental",
content_capturing="SPAN_ONLY",
)
def test_get_content_capturing_mode_parses_valid_envvar(self): # pylint: disable=no-self-use
assert get_content_capturing_mode() == ContentCapturingMode.SPAN_ONLY

@patch_env_vars(
stability_mode="gen_ai_latest_experimental", content_capturing=""
)
def test_empty_content_capturing_envvar(self): # pylint: disable=no-self-use
assert get_content_capturing_mode() == ContentCapturingMode.NO_CONTENT

@patch_env_vars(stability_mode="default", content_capturing="True")
def test_get_content_capturing_mode_raises_exception_when_semconv_stability_default(
self,
): # pylint: disable=no-self-use
with self.assertRaises(ValueError):
get_content_capturing_mode()

@patch_env_vars(
stability_mode="gen_ai_latest_experimental",
content_capturing="INVALID_VALUE",
)
def test_get_content_capturing_mode_raises_exception_on_invalid_envvar(
self,
): # pylint: disable=no-self-use
with self.assertLogs(level="WARNING") as cm:
assert (
get_content_capturing_mode() == ContentCapturingMode.NO_CONTENT
)
self.assertEqual(len(cm.output), 1)
self.assertIn("INVALID_VALUE is not a valid option for ", cm.output[0])
Loading