From d7d94ceee62f3b5a5c6d141b6234c16dd18d5c62 Mon Sep 17 00:00:00 2001 From: Xayar145 <1029229393@qq.com> Date: Sat, 22 Aug 2026 09:17:45 +0800 Subject: [PATCH 1/3] fix(otel): emit gen_ai.provider.name instead of gen_ai.system in _record_metrics when in semconv mode --- litellm/integrations/opentelemetry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 78081837ae3..01664b75f61 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1612,13 +1612,13 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): def _record_metrics(self, kwargs, response_obj, start_time, end_time): duration_s: Final = (end_time - start_time).total_seconds() params: Final = kwargs.get("litellm_params") or {} - provider: Final = params.get("custom_llm_provider", "Unknown") + provider: Final = params.get("custom_llm_provider") or kwargs.get("custom_llm_provider") or "Unknown" common_attrs = { "gen_ai.operation.name": ( self._gen_ai_operation_name(kwargs) if self._gen_ai_semconv_latest_experimental else "chat" ), - "gen_ai.system": provider, + ("gen_ai.provider.name" if self._gen_ai_semconv_latest_experimental else "gen_ai.system"): provider, "gen_ai.request.model": kwargs.get("model"), "gen_ai.framework": "litellm", } From a39b4071d2904d75a89bf55e24a949d2b0cfb879 Mon Sep 17 00:00:00 2001 From: Xayar145 <1029229393@qq.com> Date: Sat, 22 Aug 2026 09:17:49 +0800 Subject: [PATCH 2/3] test(otel): add unit test for gen_ai.provider.name metric attribute emission in semconv mode --- .../test_opentelemetry_unit_tests.py | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/tests/logging_callback_tests/test_opentelemetry_unit_tests.py b/tests/logging_callback_tests/test_opentelemetry_unit_tests.py index e8ca84a78ad..aa650f39877 100644 --- a/tests/logging_callback_tests/test_opentelemetry_unit_tests.py +++ b/tests/logging_callback_tests/test_opentelemetry_unit_tests.py @@ -9,8 +9,6 @@ import traceback from dotenv import load_dotenv load_dotenv() -import os -import asyncio sys.path.insert( 0, os.path.abspath("../..") @@ -213,3 +211,35 @@ class TestOpentelemetryUnitTests(BaseLoggingCallbackTest): mock_span.set_attribute.assert_called_with( ErrorAttributes.ERROR_MESSAGE, "Fallback error message" ) + + def test_record_metrics_semconv_provider_name(self): + """ + Test that _record_metrics emits gen_ai.provider.name under semconv mode. + """ + from litellm.integrations.opentelemetry import OpenTelemetry + from litellm.integrations.opentelemetry_utils.gen_ai_semconv import OTELSemconvCategory + + otel_integration = OpenTelemetry() + mock_histogram = MagicMock() + otel_integration._operation_duration_histogram = mock_histogram + + # Case 1: Default legacy mode + kwargs = { + "litellm_params": {"custom_llm_provider": "anthropic"}, + "model": "claude-3-5-sonnet", + } + start = datetime.now() + end = datetime.now() + otel_integration._record_metrics(kwargs, None, start, end) + call_attrs = mock_histogram.record.call_args[1]["attributes"] + assert call_attrs.get("gen_ai.system") == "anthropic" + + # Case 2: Semconv experimental mode + otel_integration.config.semconv_stability_opt_in = { + OTELSemconvCategory.GEN_AI_LATEST_EXPERIMENTAL + } + otel_integration._record_metrics(kwargs, None, start, end) + call_attrs = mock_histogram.record.call_args[1]["attributes"] + assert call_attrs.get("gen_ai.provider.name") == "anthropic" + assert "gen_ai.system" not in call_attrs + From 2d0802f5ec05f10b683242a31c25b23a1267128d Mon Sep 17 00:00:00 2001 From: Xayar145 <1029229393@qq.com> Date: Sat, 22 Aug 2026 10:48:24 +0800 Subject: [PATCH 3/3] style(otel): remove redundant case comments in unit tests --- tests/logging_callback_tests/test_opentelemetry_unit_tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/logging_callback_tests/test_opentelemetry_unit_tests.py b/tests/logging_callback_tests/test_opentelemetry_unit_tests.py index aa650f39877..15867fac1cb 100644 --- a/tests/logging_callback_tests/test_opentelemetry_unit_tests.py +++ b/tests/logging_callback_tests/test_opentelemetry_unit_tests.py @@ -223,7 +223,6 @@ class TestOpentelemetryUnitTests(BaseLoggingCallbackTest): mock_histogram = MagicMock() otel_integration._operation_duration_histogram = mock_histogram - # Case 1: Default legacy mode kwargs = { "litellm_params": {"custom_llm_provider": "anthropic"}, "model": "claude-3-5-sonnet", @@ -234,7 +233,6 @@ class TestOpentelemetryUnitTests(BaseLoggingCallbackTest): call_attrs = mock_histogram.record.call_args[1]["attributes"] assert call_attrs.get("gen_ai.system") == "anthropic" - # Case 2: Semconv experimental mode otel_integration.config.semconv_stability_opt_in = { OTELSemconvCategory.GEN_AI_LATEST_EXPERIMENTAL }