From 24fc1b037853a5c91e8425b0b7fb7fcb3c1bd789 Mon Sep 17 00:00:00 2001 From: Nagkumar Arkalgud Date: Thu, 16 Apr 2026 15:10:10 -0700 Subject: [PATCH] openai-agents-v2: use genai-utils for env var, histogram factories, and meter_provider Three improvements to align with opentelemetry-util-genai: 1. Import OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT from opentelemetry.util.genai.environment_variables instead of re-defining it locally in __init__.py. 2. Add meter_provider parameter to GenAISemanticProcessor and thread it through _init_metrics(), fixing the bug where a caller-supplied MeterProvider was silently ignored. 3. Replace inline create_histogram() calls with create_duration_histogram() and create_token_histogram() from opentelemetry.util.genai.instruments, which adds the semconv-specified explicit bucket boundaries that were previously missing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../instrumentation/openai_agents/__init__.py | 12 ++++-- .../openai_agents/span_processor.py | 38 +++++++++---------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/__init__.py b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/__init__.py index 339ccc5ba2..3dc868d3a4 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/__init__.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/__init__.py @@ -15,6 +15,9 @@ ) from opentelemetry.semconv.schemas import Schemas from opentelemetry.trace import get_tracer +from opentelemetry.util.genai.environment_variables import ( + OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, +) from .package import _instruments from .span_processor import ( @@ -38,7 +41,6 @@ logger = logging.getLogger(__name__) -_CONTENT_CAPTURE_ENV = "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT" _SYSTEM_OVERRIDE_ENV = "OTEL_INSTRUMENTATION_OPENAI_AGENTS_SYSTEM" _CAPTURE_CONTENT_ENV = "OTEL_INSTRUMENTATION_OPENAI_AGENTS_CAPTURE_CONTENT" _CAPTURE_METRICS_ENV = "OTEL_INSTRUMENTATION_OPENAI_AGENTS_CAPTURE_METRICS" @@ -147,9 +149,9 @@ def _instrument(self, **kwargs) -> None: content_override = kwargs.get("capture_message_content") if content_override is None: - content_override = os.getenv(_CONTENT_CAPTURE_ENV) or os.getenv( - _CAPTURE_CONTENT_ENV - ) + content_override = os.getenv( + OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT + ) or os.getenv(_CAPTURE_CONTENT_ENV) content_mode = _resolve_content_mode(content_override) metrics_override = kwargs.get("capture_metrics") @@ -157,6 +159,7 @@ def _instrument(self, **kwargs) -> None: metrics_override = os.getenv(_CAPTURE_METRICS_ENV) metrics_enabled = _resolve_bool(metrics_override, default=True) + meter_provider = kwargs.get("meter_provider") agent_name = kwargs.get("agent_name") agent_id = kwargs.get("agent_id") agent_description = kwargs.get("agent_description") @@ -170,6 +173,7 @@ def _instrument(self, **kwargs) -> None: include_sensitive_data=content_mode != ContentCaptureMode.NO_CONTENT, content_mode=content_mode, + meter_provider=meter_provider, metrics_enabled=metrics_enabled, agent_name=agent_name, agent_id=agent_id, diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py index 74be663701..8bddab3a46 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py @@ -55,7 +55,7 @@ ) # type: ignore[assignment] from opentelemetry.context import attach, detach -from opentelemetry.metrics import Histogram, get_meter +from opentelemetry.metrics import MeterProvider, get_meter from opentelemetry.semconv._incubating.attributes import ( gen_ai_attributes as GenAIAttributes, ) @@ -70,6 +70,10 @@ Tracer, set_span_in_context, ) +from opentelemetry.util.genai.instruments import ( + create_duration_histogram, + create_token_histogram, +) from opentelemetry.util.types import AttributeValue # Import all semantic convention constants @@ -438,6 +442,7 @@ def __init__( agent_description: Optional[str] = None, server_address: Optional[str] = None, server_port: Optional[int] = None, + meter_provider: Optional[MeterProvider] = None, metrics_enabled: bool = True, agent_name_default: Optional[str] = None, agent_id_default: Optional[str] = None, @@ -458,6 +463,7 @@ def __init__( agent_description: Description of the agent (can be overridden by env var) server_address: Server address (can be overridden by env var or base_url) server_port: Server port (can be overridden by env var or base_url) + meter_provider: Optional MeterProvider for metrics """ self._tracer = tracer self.system_name = normalize_provider(system_name) or system_name @@ -514,10 +520,10 @@ def __init__( # Metrics configuration self._metrics_enabled = metrics_enabled self._meter = None - self._duration_histogram: Optional[Histogram] = None - self._token_usage_histogram: Optional[Histogram] = None + self._duration_histogram: Optional[object] = None + self._token_usage_histogram: Optional[object] = None if self._metrics_enabled: - self._init_metrics() + self._init_metrics(meter_provider) def _get_server_attributes(self) -> dict[str, Any]: """Get server attributes from configured values.""" @@ -528,25 +534,19 @@ def _get_server_attributes(self) -> dict[str, Any]: attrs[ServerAttributes.SERVER_PORT] = self.server_port return attrs - def _init_metrics(self): + def _init_metrics(self, meter_provider: Optional[MeterProvider] = None): """Initialize metric instruments.""" - self._meter = get_meter( - "opentelemetry.instrumentation.openai_agents", "0.1.0" + from opentelemetry.instrumentation.openai_agents.version import ( # noqa: PLC0415 + __version__, ) - # Operation duration histogram - self._duration_histogram = self._meter.create_histogram( - name="gen_ai.client.operation.duration", - description="GenAI operation duration", - unit="s", - ) - - # Token usage histogram - self._token_usage_histogram = self._meter.create_histogram( - name="gen_ai.client.token.usage", - description="Number of input and output tokens used", - unit="{token}", + self._meter = get_meter( + "opentelemetry.instrumentation.openai_agents", + __version__, + meter_provider, ) + self._duration_histogram = create_duration_histogram(self._meter) + self._token_usage_histogram = create_token_histogram(self._meter) def _record_metrics( self, span: Span[Any], attributes: dict[str, AttributeValue]