From de0aff8bec40ee0d35790f09c1b272bdede8c84e Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 29 Oct 2026 19:22:37 +0000 Subject: [PATCH 1/5] Deprecate bedrock instrumentation --- .changelog/4999.deprecated | 1 + .../README.rst | 10 +- .../botocore/extensions/bedrock.py | 62 +++++++- ...ppressed_end_to_end_when_genai_active.yaml | 81 ++++++++++ .../tests/test_botocore_bedrock.py | 143 ++++++++++++++++++ 5 files changed, 290 insertions(+), 7 deletions(-) create mode 100644 .changelog/4999.deprecated create mode 100644 instrumentation/opentelemetry-instrumentation-botocore/tests/cassettes/test_converse_suppressed_end_to_end_when_genai_active.yaml diff --git a/.changelog/4999.deprecated b/.changelog/4999.deprecated new file mode 100644 index 00000000000..42e0816ea06 --- /dev/null +++ b/.changelog/4999.deprecated @@ -0,0 +1 @@ +`opentelemetry-instrumentation-botocore`: deprecate Bedrock runtime extension in favor of `opentelemetry-instrumentation-genai-bedrock` diff --git a/instrumentation/opentelemetry-instrumentation-botocore/README.rst b/instrumentation/opentelemetry-instrumentation-botocore/README.rst index 61804f740bb..7abe96e3c20 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/README.rst +++ b/instrumentation/opentelemetry-instrumentation-botocore/README.rst @@ -20,8 +20,14 @@ for the following AWS services: - SNS - SQS -Bedrock Runtime (botocore only) -******************************* +Bedrock Runtime (botocore only) [Deprecated] +******************************************** + +.. note:: + The Bedrock extension in ``opentelemetry-instrumentation-botocore`` is deprecated + and will be removed in a future release. Please use `opentelemetry-instrumentation-genai-bedrock `_ + instead. When ``opentelemetry-instrumentation-genai-bedrock`` is active, this extension + is automatically suppressed to avoid duplicate telemetry. This extension implements the GenAI semantic conventions for the following API calls: diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py index 61c54d629d2..a7ba358a8e2 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py @@ -8,9 +8,12 @@ from __future__ import annotations +import importlib +import importlib.util import io import json import logging +import warnings from timeit import default_timer from typing import Any @@ -28,6 +31,7 @@ ) from opentelemetry.instrumentation.botocore.extensions.types import ( _AttributeMapT, + _AwsSdkCallContext, _AwsSdkExtension, _BotoClientErrorT, _BotocoreInstrumentorContext, @@ -102,6 +106,26 @@ _MODEL_ID_KEY: str = "modelId" +def _is_genai_bedrock_active() -> bool: + try: + if ( + importlib.util.find_spec( + "opentelemetry.instrumentation.genai.bedrock" + ) + is None + ): + return False + mod = importlib.import_module( + "opentelemetry.instrumentation.genai.bedrock" + ) + bedrock_instrumentor_cls = getattr(mod, "BedrockInstrumentor", None) + if bedrock_instrumentor_cls is None: + return False + return bedrock_instrumentor_cls().is_instrumented_by_opentelemetry + except Exception: # pylint: disable=broad-exception-caught + return False + + class _BedrockRuntimeExtension(_AwsSdkExtension): """ This class is an extension for bool: + return not self._handled_operations + def should_end_span_on_exit(self): - return self._call_context.operation not in self._DONT_CLOSE_SPAN_ON_END_OPERATIONS + return self._call_context.operation not in self._dont_close_span_on_end_operations def setup_metrics(self, meter: Meter, metrics: dict[str, Instrument]): + if self._disabled: + return metrics[GEN_AI_CLIENT_OPERATION_DURATION] = meter.create_histogram( name=GEN_AI_CLIENT_OPERATION_DURATION, description="GenAI operation duration", @@ -158,7 +210,7 @@ def _extract_metrics_attributes(self) -> _AttributeMapT: return attributes def extract_attributes(self, attributes: _AttributeMapT): - if self._call_context.operation not in self._HANDLED_OPERATIONS: + if self._call_context.operation not in self._handled_operations: return attributes[GEN_AI_SYSTEM] = GenAiSystemValues.AWS_BEDROCK.value @@ -376,7 +428,7 @@ def _get_messages_from_input_text(self, decoded_body: dict[str, Any], input_name return [] def before_service_call(self, span: Span, instrumentor_context: _BotocoreInstrumentorContext): - if self._call_context.operation not in self._HANDLED_OPERATIONS: + if self._call_context.operation not in self._handled_operations: return capture_content = genai_capture_message_content() @@ -540,7 +592,7 @@ def on_success( result: dict[str, Any], instrumentor_context: _BotocoreInstrumentorContext, ): - if self._call_context.operation not in self._HANDLED_OPERATIONS: + if self._call_context.operation not in self._handled_operations: return capture_content = genai_capture_message_content() @@ -836,7 +888,7 @@ def on_error( exception: _BotoClientErrorT, instrumentor_context: _BotocoreInstrumentorContext, ): - if self._call_context.operation not in self._HANDLED_OPERATIONS: + if self._call_context.operation not in self._handled_operations: return span.set_status(Status(StatusCode.ERROR, str(exception))) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/cassettes/test_converse_suppressed_end_to_end_when_genai_active.yaml b/instrumentation/opentelemetry-instrumentation-botocore/tests/cassettes/test_converse_suppressed_end_to_end_when_genai_active.yaml new file mode 100644 index 00000000000..255c3832b76 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/cassettes/test_converse_suppressed_end_to_end_when_genai_active.yaml @@ -0,0 +1,81 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "user", + "content": [ + { + "text": "Say this is a test" + } + ] + } + ], + "inferenceConfig": { + "maxTokens": 10, + "temperature": 0.8, + "topP": 1, + "stopSequences": [ + "|" + ] + } + } + headers: + Content-Length: + - '170' + Content-Type: + - application/json + User-Agent: + - Boto3/1.35.56 md/Botocore#1.35.56 ua/2.0 os/macos#24.0.0 md/arch#arm64 lang/python#3.13.1 + md/pyimpl#CPython cfg/retry-mode#legacy Botocore/1.35.56 + X-Amz-Date: + - 20250206T051231Z + X-Amzn-Trace-Id: + - Root=1-c114b02b-84cec2d763a71299ae73fb9f;Parent=6f540fd45e1dc3b3;Sampled=1 + amz-sdk-invocation-id: + - a8c71d0c-ae7b-4685-b696-6ba0ded398ef + amz-sdk-request: + - attempt=1 + authorization: + - Bearer test_aws_authorization + method: POST + uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/amazon.titan-text-lite-v1/converse + response: + body: + string: |- + { + "metrics": { + "latencyMs": 608 + }, + "output": { + "message": { + "content": [ + { + "text": "Hi, how can I help you" + } + ], + "role": "assistant" + } + }, + "stopReason": "max_tokens", + "usage": { + "inputTokens": 8, + "outputTokens": 10, + "totalTokens": 18 + } + } + headers: + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Thu, 06 Feb 2025 05:12:33 GMT + Set-Cookie: test_set_cookie + x-amzn-RequestId: + - 180bdcbc-0fea-4b99-a0f0-b858dd830a83 + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py index 5f2aef727c8..0c238928686 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py @@ -7,6 +7,7 @@ import io import json +from typing import Any from unittest import mock import boto3 @@ -14,15 +15,23 @@ from botocore.eventstream import EventStream, EventStreamError from botocore.response import StreamingBody +from opentelemetry.instrumentation.botocore.extensions.bedrock import ( + _BedrockRuntimeExtension, + _is_genai_bedrock_active, +) from opentelemetry.instrumentation.botocore.extensions.bedrock_utils import ( ConverseStreamWrapper, InvokeModelWithResponseStreamWrapper, _Choice, ) +from opentelemetry.instrumentation.botocore.extensions.types import ( + _AwsSdkCallContext, +) from opentelemetry.semconv._incubating.attributes.error_attributes import ( ERROR_TYPE, ) from opentelemetry.semconv._incubating.attributes.gen_ai_attributes import ( + GEN_AI_SYSTEM, GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, ) @@ -3076,3 +3085,137 @@ def get_anthropic_tool_config(): }, } ] + + +def _create_mock_call_context( + operation: str = "Converse", + params: dict[str, Any] | None = None, +) -> _AwsSdkCallContext: + mock_client = mock.MagicMock() + mock_client.meta.region_name = "us-east-1" + mock_client.meta.endpoint_url = "https://bedrock-runtime.us-east-1.amazonaws.com" + mock_client.meta.service_model.service_name = "bedrock-runtime" + mock_client.meta.service_model.service_id = "Bedrock Runtime" + mock_client.meta.service_model.api_version = "2023-09-30" + return _AwsSdkCallContext(mock_client, (operation, params or {})) + + +def test_bedrock_runtime_extension_deprecation_warning(): + call_context = _create_mock_call_context() + with pytest.deprecated_call() as warning_info: + _BedrockRuntimeExtension(call_context) + + assert any( + "The Bedrock extension in 'opentelemetry-instrumentation-botocore' is deprecated" + in str(w.message) + for w in warning_info + ) + + +def test_is_genai_bedrock_active(): + with mock.patch("importlib.util.find_spec", return_value=None): + assert not _is_genai_bedrock_active() + + mock_instrumentor_cls = mock.MagicMock() + mock_instrumentor_cls.return_value.is_instrumented_by_opentelemetry = False + with ( + mock.patch("importlib.util.find_spec", return_value=mock.MagicMock()), + mock.patch.dict( + "sys.modules", + {"opentelemetry.instrumentation.genai.bedrock": mock.MagicMock(BedrockInstrumentor=mock_instrumentor_cls)}, + ), + ): + assert not _is_genai_bedrock_active() + + mock_instrumentor_cls.return_value.is_instrumented_by_opentelemetry = True + with ( + mock.patch("importlib.util.find_spec", return_value=mock.MagicMock()), + mock.patch.dict( + "sys.modules", + {"opentelemetry.instrumentation.genai.bedrock": mock.MagicMock(BedrockInstrumentor=mock_instrumentor_cls)}, + ), + ): + assert _is_genai_bedrock_active() + + +def test_bedrock_extension_suppressed_when_genai_active(): + call_context = _create_mock_call_context( + operation="ConverseStream", + params={"modelId": "amazon.titan-text-lite-v1"}, + ) + with mock.patch( + "opentelemetry.instrumentation.botocore.extensions.bedrock._is_genai_bedrock_active", + return_value=True, + ): + extension = _BedrockRuntimeExtension(call_context) + + assert extension._disabled is True + # When disabled, should_end_span_on_exit should be True (standard botocore behavior) + assert extension.should_end_span_on_exit() is True + + # setup_metrics should be a no-op + metrics: dict[str, Any] = {} + extension.setup_metrics(mock.MagicMock(), metrics) + assert len(metrics) == 0 + + # extract_attributes should be a no-op + attributes: dict[str, Any] = {"rpc.system": "aws-api"} + extension.extract_attributes(attributes) + assert attributes == {"rpc.system": "aws-api"} + + # before_service_call should be a no-op + mock_span = mock.MagicMock() + mock_ctx = mock.MagicMock() + extension.before_service_call(mock_span, mock_ctx) + mock_ctx.logger.emit.assert_not_called() + mock_span.update_name.assert_not_called() + + # on_success should be a no-op (no stream wrapper, no events, no metrics) + result = {"stream": mock.MagicMock(spec=EventStream)} + extension.on_success(mock_span, result, mock_ctx) + mock_ctx.logger.emit.assert_not_called() + assert not isinstance(result["stream"], ConverseStreamWrapper) + + # on_error should be a no-op + mock_error = mock.MagicMock() + extension.on_error(mock_span, mock_error, mock_ctx) + mock_span.set_status.assert_not_called() + + +@pytest.mark.skipif(BOTO3_VERSION < (1, 35, 56), reason="Converse API not available") +@pytest.mark.vcr() +def test_converse_suppressed_end_to_end_when_genai_active( + span_exporter, + log_exporter, + metric_reader, + bedrock_runtime_client, + instrument_with_content, +): + with mock.patch( + "opentelemetry.instrumentation.botocore.extensions.bedrock._is_genai_bedrock_active", + return_value=True, + ): + messages = [{"role": "user", "content": [{"text": "Say this is a test"}]}] + response = bedrock_runtime_client.converse( + messages=messages, + modelId="amazon.titan-text-lite-v1", + inferenceConfig={ + "maxTokens": 10, + "temperature": 0.8, + "topP": 1, + "stopSequences": ["|"], + }, + ) + assert response is not None + + (span,) = span_exporter.get_finished_spans() + # Span name should remain the raw botocore RPC span name + assert span.name == "Bedrock Runtime.Converse" + assert GEN_AI_SYSTEM not in span.attributes + assert "gen_ai.request.model" not in span.attributes + + # No gen_ai log events emitted + assert len(log_exporter.get_finished_logs()) == 0 + + # No gen_ai metrics recorded + assert metric_reader.get_metrics_data() is None From 2837d60c88ad1f38c8cdfac152f041401f5cfa84 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 29 Oct 2026 19:26:57 +0000 Subject: [PATCH 2/5] chore(botocore): number changelog fragment for PR 5266 --- .changelog/{4999.deprecated => 5266.deprecated} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changelog/{4999.deprecated => 5266.deprecated} (100%) diff --git a/.changelog/4999.deprecated b/.changelog/5266.deprecated similarity index 100% rename from .changelog/4999.deprecated rename to .changelog/5266.deprecated From 0ce2bb6a36ec41d593e63f907724a2dc14f2ddf4 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 29 Oct 2026 20:02:15 +0000 Subject: [PATCH 3/5] style(botocore): format bedrock extension and tests with ruff --- .../botocore/extensions/bedrock.py | 15 +++------------ .../tests/test_botocore_bedrock.py | 3 +-- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py index a7ba358a8e2..5a9c58baf14 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py @@ -108,16 +108,9 @@ def _is_genai_bedrock_active() -> bool: try: - if ( - importlib.util.find_spec( - "opentelemetry.instrumentation.genai.bedrock" - ) - is None - ): + if importlib.util.find_spec("opentelemetry.instrumentation.genai.bedrock") is None: return False - mod = importlib.import_module( - "opentelemetry.instrumentation.genai.bedrock" - ) + mod = importlib.import_module("opentelemetry.instrumentation.genai.bedrock") bedrock_instrumentor_cls = getattr(mod, "BedrockInstrumentor", None) if bedrock_instrumentor_cls is None: return False @@ -162,9 +155,7 @@ def __init__(self, call_context: _AwsSdkCallContext): self._dont_close_span_on_end_operations: set[str] = set() else: self._handled_operations = self._HANDLED_OPERATIONS - self._dont_close_span_on_end_operations = ( - self._DONT_CLOSE_SPAN_ON_END_OPERATIONS - ) + self._dont_close_span_on_end_operations = self._DONT_CLOSE_SPAN_ON_END_OPERATIONS @property def _disabled(self) -> bool: diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py index 0c238928686..470340a8882 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py @@ -3106,8 +3106,7 @@ def test_bedrock_runtime_extension_deprecation_warning(): _BedrockRuntimeExtension(call_context) assert any( - "The Bedrock extension in 'opentelemetry-instrumentation-botocore' is deprecated" - in str(w.message) + "The Bedrock extension in 'opentelemetry-instrumentation-botocore' is deprecated" in str(w.message) for w in warning_info ) From a624e0c675e173a0aa3980b652bd1297a80f9b06 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Wed, 4 Nov 2026 14:33:38 +0000 Subject: [PATCH 4/5] Address comments --- .../botocore/extensions/bedrock.py | 14 ++++------- .../tests/test_botocore_bedrock.py | 24 ++++--------------- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py index 5a9c58baf14..991eba14181 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py @@ -8,8 +8,7 @@ from __future__ import annotations -import importlib -import importlib.util +import importlib.metadata import io import json import logging @@ -108,14 +107,9 @@ def _is_genai_bedrock_active() -> bool: try: - if importlib.util.find_spec("opentelemetry.instrumentation.genai.bedrock") is None: - return False - mod = importlib.import_module("opentelemetry.instrumentation.genai.bedrock") - bedrock_instrumentor_cls = getattr(mod, "BedrockInstrumentor", None) - if bedrock_instrumentor_cls is None: - return False - return bedrock_instrumentor_cls().is_instrumented_by_opentelemetry - except Exception: # pylint: disable=broad-exception-caught + importlib.metadata.version("opentelemetry-instrumentation-genai-bedrock") + return True + except importlib.metadata.PackageNotFoundError: return False diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py index 470340a8882..90ad0da241b 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py @@ -5,6 +5,7 @@ from __future__ import annotations +import importlib.metadata import io import json from typing import Any @@ -3112,28 +3113,13 @@ def test_bedrock_runtime_extension_deprecation_warning(): def test_is_genai_bedrock_active(): - with mock.patch("importlib.util.find_spec", return_value=None): - assert not _is_genai_bedrock_active() - - mock_instrumentor_cls = mock.MagicMock() - mock_instrumentor_cls.return_value.is_instrumented_by_opentelemetry = False - with ( - mock.patch("importlib.util.find_spec", return_value=mock.MagicMock()), - mock.patch.dict( - "sys.modules", - {"opentelemetry.instrumentation.genai.bedrock": mock.MagicMock(BedrockInstrumentor=mock_instrumentor_cls)}, - ), + with mock.patch( + "importlib.metadata.version", + side_effect=importlib.metadata.PackageNotFoundError, ): assert not _is_genai_bedrock_active() - mock_instrumentor_cls.return_value.is_instrumented_by_opentelemetry = True - with ( - mock.patch("importlib.util.find_spec", return_value=mock.MagicMock()), - mock.patch.dict( - "sys.modules", - {"opentelemetry.instrumentation.genai.bedrock": mock.MagicMock(BedrockInstrumentor=mock_instrumentor_cls)}, - ), - ): + with mock.patch("importlib.metadata.version", return_value="0.1.0"): assert _is_genai_bedrock_active() From 6341374c409d514de6736bc8d397f196b250d489 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 5 Nov 2026 15:05:08 +0000 Subject: [PATCH 5/5] Address comments --- .../README.rst | 6 +++--- .../instrumentation/botocore/extensions/bedrock.py | 14 +++++++------- .../tests/test_botocore_bedrock.py | 12 +++++++++--- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/README.rst b/instrumentation/opentelemetry-instrumentation-botocore/README.rst index 7abe96e3c20..fa0001360f6 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/README.rst +++ b/instrumentation/opentelemetry-instrumentation-botocore/README.rst @@ -14,7 +14,7 @@ Extensions The instrumentation supports creating extensions for AWS services for enriching what is collected. We have extensions for the following AWS services: -- Bedrock Runtime (botocore only) +- Bedrock Runtime (botocore only) [Deprecated] - DynamoDB - Lambda - SNS @@ -25,8 +25,8 @@ Bedrock Runtime (botocore only) [Deprecated] .. note:: The Bedrock extension in ``opentelemetry-instrumentation-botocore`` is deprecated - and will be removed in a future release. Please use `opentelemetry-instrumentation-genai-bedrock `_ - instead. When ``opentelemetry-instrumentation-genai-bedrock`` is active, this extension + and will be removed in a future release. Please use `opentelemetry-instrumentation-genai-bedrock `_. + When ``opentelemetry-instrumentation-genai-bedrock`` is active, this extension is automatically suppressed to avoid duplicate telemetry. This extension implements the GenAI semantic conventions for the following API calls: diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py index 991eba14181..a60a1e4c7f5 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py @@ -133,13 +133,6 @@ class _BedrockRuntimeExtension(_AwsSdkExtension): def __init__(self, call_context: _AwsSdkCallContext): super().__init__(call_context) - warnings.warn( - "The Bedrock extension in 'opentelemetry-instrumentation-botocore' is deprecated " - "and will be removed in a future release. " - "Please use 'opentelemetry-instrumentation-genai-bedrock' instead.", - category=DeprecationWarning, - stacklevel=2, - ) if _is_genai_bedrock_active(): _logger.debug( "opentelemetry-instrumentation-genai-bedrock is active; " @@ -148,6 +141,13 @@ def __init__(self, call_context: _AwsSdkCallContext): self._handled_operations: set[str] = set() self._dont_close_span_on_end_operations: set[str] = set() else: + warnings.warn( + "The Bedrock extension in 'opentelemetry-instrumentation-botocore' is deprecated " + "and will be removed in a future release. " + "Please install 'opentelemetry-instrumentation-genai-bedrock' that would take over this extension.", + category=DeprecationWarning, + stacklevel=2, + ) self._handled_operations = self._HANDLED_OPERATIONS self._dont_close_span_on_end_operations = self._DONT_CLOSE_SPAN_ON_END_OPERATIONS diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py index 90ad0da241b..c0bfd8142c7 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py @@ -8,6 +8,7 @@ import importlib.metadata import io import json +import warnings from typing import Any from unittest import mock @@ -3128,12 +3129,17 @@ def test_bedrock_extension_suppressed_when_genai_active(): operation="ConverseStream", params={"modelId": "amazon.titan-text-lite-v1"}, ) - with mock.patch( - "opentelemetry.instrumentation.botocore.extensions.bedrock._is_genai_bedrock_active", - return_value=True, + with ( + mock.patch( + "opentelemetry.instrumentation.botocore.extensions.bedrock._is_genai_bedrock_active", + return_value=True, + ), + warnings.catch_warnings(record=True) as recorded_warnings, ): + warnings.simplefilter("always", DeprecationWarning) extension = _BedrockRuntimeExtension(call_context) + assert not any(issubclass(w.category, DeprecationWarning) for w in recorded_warnings) assert extension._disabled is True # When disabled, should_end_span_on_exit should be True (standard botocore behavior) assert extension.should_end_span_on_exit() is True