Skip to content

Commit 40ba237

Browse files
feat(bedrock): Set token attributes for nonstreaming responses
1 parent da37f0a commit 40ba237

4 files changed

Lines changed: 2386 additions & 2321 deletions

File tree

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ typing = [
8282
"openai-agents>=0.22.0",
8383
"mistralai>=2.10.1",
8484
"typesafe-sdk>=0.7.5",
85+
"mypy-boto3-bedrock-runtime>=1.43.62",
8586
]
8687
test = [
8788
"dataclasses ; python_full_version < '3.7'",

‎sentry_sdk/integrations/boto3/_services/bedrock.py‎

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
from typing import TYPE_CHECKING
1+
from typing import TYPE_CHECKING, cast
22

33
from sentry_sdk.consts import OP, SPANDATA
44
from sentry_sdk.integrations.boto3._services.base import _ServiceExtension
55

66
if TYPE_CHECKING:
77
from typing import Any, Optional
88

9+
from mypy_boto3_bedrock_runtime.type_defs import ConverseResponseTypeDef
10+
911
from sentry_sdk._types import Attributes
1012
from sentry_sdk.integrations.boto3._context import AwsCallContext
1113

@@ -39,4 +41,33 @@ def get_response_attributes(
3941
ctx: "AwsCallContext",
4042
response: "Any",
4143
) -> "Attributes":
42-
return {}
44+
if ctx.operation_name != "Converse":
45+
return {}
46+
47+
attributes: "Attributes" = {}
48+
49+
usage = cast("ConverseResponseTypeDef", response)["usage"]
50+
51+
input_tokens = usage["inputTokens"]
52+
if input_tokens is not None:
53+
attributes[SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] = input_tokens
54+
55+
cache_read_tokens = usage.get("cacheReadInputTokens")
56+
if cache_read_tokens is not None:
57+
attributes[SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] = cache_read_tokens
58+
59+
cache_write_tokens = usage.get("cacheWriteInputTokens")
60+
if cache_write_tokens is not None:
61+
attributes[SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE] = (
62+
cache_write_tokens
63+
)
64+
65+
output_tokens = usage["outputTokens"]
66+
if output_tokens is not None:
67+
attributes[SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] = output_tokens
68+
69+
total_tokens = usage["totalTokens"]
70+
if total_tokens is not None:
71+
attributes[SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] = total_tokens
72+
73+
return attributes

‎tests/integrations/boto3/test_bedrock.py‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import boto3
2+
import botocore
23
import pytest
34
from botocore.exceptions import ClientError
45
from botocore.loaders import Loader
56
from botocore.stub import Stubber
7+
from packaging.version import Version
68

79
import sentry_sdk
810
from sentry_sdk.consts import OP, SPANDATA
911
from sentry_sdk.integrations.boto3 import Boto3Integration
1012

13+
BOTOCORE_VERSION = Version(botocore.__version__)
14+
1115
pytestmark = pytest.mark.skipif(
1216
"bedrock-runtime" not in Loader().list_available_services("service-2"),
1317
reason="botocore is too old for bedrock-runtime.",
@@ -16,10 +20,15 @@
1620

1721
@pytest.fixture
1822
def converse_response():
23+
usage = {"inputTokens": 10, "outputTokens": 20, "totalTokens": 30}
24+
if BOTOCORE_VERSION >= Version("1.37.24"):
25+
usage["cacheReadInputTokens"] = 80
26+
usage["cacheWriteInputTokens"] = 20
27+
1928
return {
2029
"output": {"message": {"role": "assistant", "content": [{"text": "Hello"}]}},
2130
"stopReason": "end_turn",
22-
"usage": {"inputTokens": 50, "outputTokens": 30, "totalTokens": 80},
31+
"usage": usage,
2332
"metrics": {"latencyMs": 500},
2433
}
2534

@@ -68,6 +77,13 @@ def test_converse(
6877
assert span["attributes"][SPANDATA.GEN_AI_PROVIDER_NAME] == "aws.bedrock"
6978
assert span["attributes"][SPANDATA.GEN_AI_OPERATION_NAME] == "chat"
7079
assert span["attributes"][SPANDATA.GEN_AI_RESPONSE_STREAMING] is False
80+
assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 10
81+
assert span["attributes"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 20
82+
assert span["attributes"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 30
83+
if BOTOCORE_VERSION < Version("1.37.24"):
84+
return
85+
assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] == 80
86+
assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE] == 20
7187
else:
7288
events = capture_events()
7389

@@ -83,6 +99,13 @@ def test_converse(
8399
assert span["data"][SPANDATA.GEN_AI_PROVIDER_NAME] == "aws.bedrock"
84100
assert span["data"][SPANDATA.GEN_AI_OPERATION_NAME] == "chat"
85101
assert span["data"][SPANDATA.GEN_AI_RESPONSE_STREAMING] is False
102+
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 10
103+
assert span["data"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 20
104+
assert span["data"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 30
105+
if BOTOCORE_VERSION < Version("1.37.24"):
106+
return
107+
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] == 80
108+
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE] == 20
86109

87110

88111
@pytest.mark.parametrize("span_streaming", [True, False])

0 commit comments

Comments
 (0)