Skip to content
Merged
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
@@ -0,0 +1 @@
Add instrumentation for ``Converse`` and ``ConverseStream``.
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
OpenTelemetry Amazon Bedrock Instrumentation
============================================

This package provides the setup for instrumenting Amazon Bedrock with OpenTelemetry
Generative AI semantic conventions. Amazon Bedrock operation instrumentation will be
added in follow-up changes.
This package provides OpenTelemetry instrumentation for Amazon Bedrock (via the AWS SDK for Python, ``boto3``),
implementing the OpenTelemetry Generative AI semantic conventions.

Supported Operations
--------------------

* Synchronous chat via the Converse API (``client.converse``)
* Streaming chat via the ConverseStream API (``client.converse_stream``)

Installation
------------
Expand All @@ -15,12 +20,21 @@ Installation
Usage
-----

::
.. code-block:: python

import boto3
from opentelemetry.instrumentation.genai.bedrock import BedrockInstrumentor

# Enable instrumentation
BedrockInstrumentor().instrument()

# Use Bedrock runtime client normally
client = boto3.client("bedrock-runtime", region_name="us-east-1")
response = client.converse(
modelId="amazon.nova-micro-v1:0",
messages=[{"role": "user", "content": [{"text": "Hello, Bedrock!"}]}],
)

Configuration
-------------

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

# pylint: skip-file
"""Same as main.py, but instruments Amazon Bedrock with a custom CompletionHook
that prints prompts and completions to stdout.

Run with: dotenv run -- python custom_hook.py
"""

import boto3

from opentelemetry import _logs, metrics, trace
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
OTLPLogExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
OTLPMetricExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
from opentelemetry.instrumentation.genai.bedrock import BedrockInstrumentor
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.util.genai.completion_hook import CompletionHook
from opentelemetry.util.genai.types import (
InputMessage,
MessagePart,
OutputMessage,
ToolDefinition,
)


class PrintCompletionHook(CompletionHook):
"""Minimal CompletionHook that prints inputs/outputs to stdout.

Real hooks typically forward content to external storage (object store,
database, etc.) and record reference URIs on the span/log_record.
"""

def on_completion(
self,
*,
inputs: list[InputMessage],
outputs: list[OutputMessage],
system_instruction: list[MessagePart],
tool_definitions: list[ToolDefinition] | None = None,
span=None,
log_record=None,
) -> None:
print(f"[hook] inputs: {inputs}")
print(f"[hook] outputs: {outputs}")


trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(OTLPSpanExporter())
)

_logs.set_logger_provider(LoggerProvider())
_logs.get_logger_provider().add_log_record_processor(
BatchLogRecordProcessor(OTLPLogExporter())
)

metrics.set_meter_provider(
MeterProvider(
metric_readers=[
PeriodicExportingMetricReader(OTLPMetricExporter()),
]
)
)

BedrockInstrumentor().instrument(completion_hook=PrintCompletionHook())


def main():
client = boto3.client("bedrock-runtime", region_name="us-east-1")
response = client.converse(
modelId="amazon.nova-micro-v1:0",
messages=[
{
"role": "user",
"content": [{"text": "Write a short poem on OpenTelemetry."}],
}
],
)
print(response)


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# SPDX-License-Identifier: Apache-2.0

# pylint: skip-file
import json

import boto3

Expand Down Expand Up @@ -37,16 +36,16 @@

def main():
client = boto3.client("bedrock-runtime", region_name="us-east-1")
response = client.invoke_model(
modelId="amazon.titan-text-lite-v1",
body=json.dumps(
response = client.converse(
modelId="amazon.nova-micro-v1:0",
messages=[
{
"inputText": "Write a short poem on OpenTelemetry.",
"role": "user",
"content": [{"text": "Write a short poem on OpenTelemetry."}],
}
),
],
)
result = json.loads(response["body"].read())
print(result)
print(response)


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@
# SPDX-License-Identifier: Apache-2.0

# pylint: skip-file
import json

import boto3


def main():
client = boto3.client("bedrock-runtime", region_name="us-east-1")
response = client.invoke_model(
modelId="amazon.titan-text-lite-v1",
body=json.dumps(
response = client.converse(
modelId="amazon.nova-micro-v1:0",
messages=[
{
"inputText": "Write a short poem on OpenTelemetry.",
"role": "user",
"content": [{"text": "Write a short poem on OpenTelemetry."}],
}
),
],
)
result = json.loads(response["body"].read())
print(result)
print(response)


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ dependencies = [
"opentelemetry-api ~= 1.43",
"opentelemetry-instrumentation >= 0.64b0, <1",
"opentelemetry-semantic-conventions >= 0.64b0, <1",
"opentelemetry-util-genai >= 1.0b0, <2",
"opentelemetry-util-genai >= 1.2b0.dev, <2",
]

[project.optional-dependencies]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
from collections.abc import Collection
from typing import Any

from opentelemetry.instrumentation.genai.bedrock.package import _instruments
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.util.genai.completion_hook import load_completion_hook
from opentelemetry.util.genai.handler import TelemetryHandler

from .package import _instruments
from .patch import patch_bedrock, unpatch_bedrock

__all__ = ["BedrockInstrumentor"]


Expand All @@ -35,9 +37,9 @@ def _instrument(self, **kwargs: Any) -> None:
logger_provider=kwargs.get("logger_provider"),
completion_hook=completion_hook,
)
# Amazon Bedrock patching will be added in a follow-up change.
patch_bedrock(self._handler)

def _uninstrument(self, **kwargs: Any) -> None:
"""Disable Amazon Bedrock instrumentation."""
unpatch_bedrock()
self._handler = None
# Amazon Bedrock unpatching will be added in a follow-up change.
Loading