Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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 @@ -75,48 +75,6 @@ To uninstrument clients, call the uninstrument method:
# Uninstrument all clients
OpenAIInstrumentor().uninstrument()

Bucket Boundaries
-----------------

This section describes the explicit bucket boundaries for metrics such as token usage and operation duration, and guides users to create Views to implement them according to the semantic conventions.

The bucket boundaries are defined as follows:

- For `gen_ai.client.token.usage`: [1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864]
- For `gen_ai.client.operation.duration`: [0.01, 0.02, 0.04, 0.08, 0.16, 0.32, 0.64, 1.28, 2.56, 5.12, 10.24, 20.48, 40.96, 81.92]

To implement these bucket boundaries, you can create Views in your OpenTelemetry SDK setup. Here is an example:

.. code-block:: python

from opentelemetry.sdk.metrics import MeterProvider, View
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.metrics.aggregation import ExplicitBucketHistogramAggregation

views = [
View(
instrument_name="gen_ai.client.token.usage",
aggregation=ExplicitBucketHistogramAggregation([1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864]),
),
View(
instrument_name="gen_ai.client.operation.duration",
aggregation=ExplicitBucketHistogramAggregation([0.01, 0.02, 0.04, 0.08, 0.16, 0.32, 0.64, 1.28, 2.56, 5.12, 10.24, 20.48, 40.96, 81.92]),
),
]

metric_exporter = OTLPMetricExporter(endpoint="http://localhost:4317")
metric_reader = PeriodicExportingMetricReader(metric_exporter)
provider = MeterProvider(
metric_readers=[metric_reader],
views=views
)

from opentelemetry.sdk.metrics import set_meter_provider
set_meter_provider(provider)

For more details, refer to the `OpenTelemetry GenAI Metrics documentation <https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-metrics/>`_.

References
----------
* `OpenTelemetry OpenAI Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/openai/openai.html>`_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ openai~=1.57.3

opentelemetry-sdk~=1.29.0
opentelemetry-exporter-otlp-proto-grpc~=1.29.0
opentelemetry-instrumentation-openai-v2~=2.0b0
opentelemetry-instrumentation-openai-v2~=2.2b0
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ classifiers = [
"Programming Language :: Python :: 3.13",
]
dependencies = [
"opentelemetry-api ~= 1.28",
"opentelemetry-instrumentation ~= 0.49b0",
"opentelemetry-semantic-conventions ~= 0.49b0"
"opentelemetry-api ~= 1.30",
"opentelemetry-instrumentation ~= 0.51b0",
"opentelemetry-semantic-conventions ~= 0.51b0"
]

[project.optional-dependencies]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,52 @@
from opentelemetry.metrics import Histogram, Meter
from opentelemetry.semconv._incubating.metrics import gen_ai_metrics

_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS = [
0.01,
0.02,
0.04,
0.08,
0.16,
0.32,
0.64,
1.28,
2.56,
5.12,
10.24,
20.48,
40.96,
81.92,
]

_GEN_AI_CLIENT_TOKEN_USAGE_BUCKETS = [
1,
4,
16,
64,
256,
1024,
4096,
16384,
65536,
262144,
1048576,
4194304,
16777216,
67108864,
]


class Instruments:
def __init__(self, meter):
self.operation_duration_histogram = (
gen_ai_metrics.create_gen_ai_client_operation_duration(meter)
def __init__(self, meter: Meter):
self.operation_duration_histogram: Histogram = meter.create_histogram(
name=gen_ai_metrics.GEN_AI_CLIENT_OPERATION_DURATION,
description="GenAI operation duration",
unit="s",
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS,
)
self.token_usage_histogram = (
gen_ai_metrics.create_gen_ai_client_token_usage(meter)
self.token_usage_histogram: Histogram = meter.create_histogram(
name=gen_ai_metrics.GEN_AI_CLIENT_TOKEN_USAGE,
description="Measures number of input and output tokens used",
unit="{token}",
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_TOKEN_USAGE_BUCKETS,
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ pytest-vcr==1.0.2
pytest-asyncio==0.21.0
wrapt==1.16.0
opentelemetry-exporter-otlp-proto-http~=1.28
opentelemetry-api==1.28 # when updating, also update in pyproject.toml
opentelemetry-sdk==1.28 # when updating, also update in pyproject.toml
opentelemetry-semantic-conventions==0.49b0 # when updating, also update in pyproject.toml
opentelemetry-api==1.30 # when updating, also update in pyproject.toml
opentelemetry-sdk==1.30 # when updating, also update in pyproject.toml
opentelemetry-semantic-conventions==0.51b0 # when updating, also update in pyproject.toml

-e instrumentation-genai/opentelemetry-instrumentation-openai-v2
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,11 @@
SimpleLogRecordProcessor,
)
from opentelemetry.sdk.metrics import (
Histogram,
MeterProvider,
)
from opentelemetry.sdk.metrics.export import (
InMemoryMetricReader,
)
from opentelemetry.sdk.metrics.view import (
ExplicitBucketHistogramAggregation,
View,
)
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
Expand Down Expand Up @@ -72,55 +67,8 @@ def fixture_event_logger_provider(log_exporter):

@pytest.fixture(scope="function", name="meter_provider")
def fixture_meter_provider(metric_reader):
token_usage_histogram_view = View(
instrument_type=Histogram,
instrument_name="gen_ai.client.token.usage",
aggregation=ExplicitBucketHistogramAggregation(
boundaries=[
1,
4,
16,
64,
256,
1024,
4096,
16384,
65536,
262144,
1048576,
4194304,
16777216,
67108864,
]
),
)

duration_histogram_view = View(
instrument_type=Histogram,
instrument_name="gen_ai.client.operation.duration",
aggregation=ExplicitBucketHistogramAggregation(
boundaries=[
0.01,
0.02,
0.04,
0.08,
0.16,
0.32,
0.64,
1.28,
2.56,
5.12,
10.24,
20.48,
40.96,
81.92,
]
),
)

meter_provider = MeterProvider(
metric_readers=[metric_reader],
views=[token_usage_histogram_view, duration_histogram_view],
)

return meter_provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,39 @@
)
from opentelemetry.semconv._incubating.metrics import gen_ai_metrics

_DURATION_BUCKETS = (
0.01,
0.02,
0.04,
0.08,
0.16,
0.32,
0.64,
1.28,
2.56,
5.12,
10.24,
20.48,
40.96,
81.92,
)
_TOKEN_USAGE_BUCKETS = (
1,
4,
16,
64,
256,
1024,
4096,
16384,
65536,
262144,
1048576,
4194304,
16777216,
67108864,
)


def assert_all_metric_attributes(data_point):
assert GenAIAttributes.GEN_AI_OPERATION_NAME in data_point.attributes
Expand Down Expand Up @@ -77,8 +110,11 @@ def test_chat_completion_metrics(
None,
)
assert duration_metric is not None
assert duration_metric.data.data_points[0].sum > 0
assert_all_metric_attributes(duration_metric.data.data_points[0])

duration_point = duration_metric.data.data_points[0]
assert duration_point .sum > 0
assert_all_metric_attributes(duration_point )
assert duration_point .explicit_bounds == _DURATION_BUCKETS

token_usage_metric = next(
(
Expand All @@ -101,7 +137,8 @@ def test_chat_completion_metrics(
)
assert input_token_usage is not None
assert input_token_usage.sum == 12
# assert against buckets [1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864]

assert input_token_usage.explicit_bounds == _TOKEN_USAGE_BUCKETS
assert input_token_usage.bucket_counts[2] == 1
assert_all_metric_attributes(input_token_usage)

Expand Down
Loading