Skip to content
Open
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 @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

- Import `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` from
`opentelemetry.util.genai.environment_variables` instead of re-defining it locally;
add `meter_provider` parameter to `GenAISemanticProcessor` so a caller-supplied
provider is no longer silently ignored; use `create_duration_histogram` and
`create_token_histogram` from `opentelemetry.util.genai.instruments` to apply
semconv-specified histogram bucket boundaries.
([#4456](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4456))
- Align AgentSpanData test stubs and span processor with real OpenAI Agents SDK;
remove non-existent `operation`, `description`, `agent_id`, and `model` fields.
([#4229](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4229))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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"
Expand Down Expand Up @@ -147,16 +149,17 @@ 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")
if metrics_override is 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")
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
from typing import TYPE_CHECKING, Any, Dict, Iterator, Optional, Sequence
from urllib.parse import urlparse

from opentelemetry.util.genai.utils import gen_ai_json_dumps

try:
from agents.tracing import Span, Trace, TracingProcessor
from agents.tracing.span_data import (
Expand Down Expand Up @@ -55,7 +53,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,
)
Expand All @@ -70,8 +68,15 @@
Tracer,
set_span_in_context,
)
from opentelemetry.util.genai.instruments import (
create_duration_histogram,
create_token_histogram,
)
from opentelemetry.util.genai.utils import gen_ai_json_dumps
from opentelemetry.util.types import AttributeValue

from .version import __version__

# Import all semantic convention constants
# ---- GenAI semantic convention helpers (embedded from constants.py) ----

Expand Down Expand Up @@ -439,6 +444,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,
Expand All @@ -459,6 +465,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
Expand Down Expand Up @@ -515,10 +522,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
Comment on lines 524 to +526
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

_duration_histogram and _token_usage_histogram were widened from Optional[Histogram] to Optional[object], which loses type safety even though create_duration_histogram/create_token_histogram return Histogram. Consider importing Histogram and keeping these attributes typed as Optional[Histogram] (or a more specific protocol) so static checkers can validate .record() usage.

Copilot uses AI. Check for mistakes.
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."""
Expand All @@ -529,25 +536,15 @@ 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"
)

# 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}",
"opentelemetry.instrumentation.openai_agents",
__version__,
meter_provider,
)
Comment on lines 541 to 545
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The new meter_provider plumbing fixes an important behavior change (caller-supplied providers were previously ignored), but it doesn’t look like there’s a unit test asserting the passed meter_provider is actually used by get_meter(...). Adding a small test with a stub MeterProvider (tracking get_meter calls) would prevent regressions.

Copilot uses AI. Check for mistakes.
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]
Expand Down
Loading