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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ typing = [
"openai-agents>=0.22.0",
"mistralai>=2.10.1",
"typesafe-sdk>=0.7.5",
"mypy-boto3-bedrock-runtime>=1.43.62",
]
test = [
"dataclasses ; python_full_version < '3.7'",
Expand Down
31 changes: 29 additions & 2 deletions sentry_sdk/integrations/boto3/_services/bedrock.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import json
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, cast

import sentry_sdk
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations.boto3._services.base import _ServiceExtension
from sentry_sdk.utils import has_data_collection_enabled

if TYPE_CHECKING:
from typing import Any, Optional

from mypy_boto3_bedrock_runtime.type_defs import ConverseResponseTypeDef

Check warning on line 12 in sentry_sdk/integrations/boto3/_services/bedrock.py

View check run for this annotation

@sentry/warden / warden: code-review

Total token attribute omits cache tokens included in input usage

In `get_response_attributes`, `GEN_AI_USAGE_TOTAL_TOKENS` is copied from `usage["totalTokens"]` before cached and cache-write tokens are added to `input_tokens`. The resulting total can therefore disagree with the reported input and output tokens; set it to the normalized `input_tokens + output_tokens` after cache-token adjustments.

from sentry_sdk._types import Attributes, ToolDefinition
from sentry_sdk.integrations.boto3._context import AwsCallContext

Expand Down Expand Up @@ -122,4 +124,29 @@
ctx: "AwsCallContext",
response: "Any",
) -> "Attributes":
return {}
if ctx.operation_name != "Converse" or "Error" in response:
return {}

usage = cast("ConverseResponseTypeDef", response)["usage"]

input_tokens = usage["inputTokens"]
attributes: "Attributes" = {
SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS: usage["outputTokens"],
SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS: usage["totalTokens"],
}

Check warning on line 136 in sentry_sdk/integrations/boto3/_services/bedrock.py

View check run for this annotation

@sentry/warden / warden: code-review

[BXV-G3H] Total token attribute omits cache tokens included in input usage (additional location)

In `get_response_attributes`, `GEN_AI_USAGE_TOTAL_TOKENS` is copied from `usage["totalTokens"]` before cached and cache-write tokens are added to `input_tokens`. The resulting total can therefore disagree with the reported input and output tokens; set it to the normalized `input_tokens + output_tokens` after cache-token adjustments.

cache_read_tokens = usage.get("cacheReadInputTokens")
if cache_read_tokens is not None:
input_tokens += cache_read_tokens
attributes[SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] = cache_read_tokens

cache_write_tokens = usage.get("cacheWriteInputTokens")
if cache_write_tokens is not None:
input_tokens += cache_write_tokens
attributes[SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE] = (
cache_write_tokens
)
Comment thread
alexander-alderman-webb marked this conversation as resolved.
Resolved

attributes[SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] = input_tokens
Comment thread
alexander-alderman-webb marked this conversation as resolved.
Resolved

return attributes
25 changes: 24 additions & 1 deletion tests/integrations/boto3/test_bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@

@pytest.fixture
def converse_response():
usage = {"inputTokens": 10, "outputTokens": 20, "totalTokens": 30}
if BOTOCORE_VERSION >= Version("1.37.24"):
usage["cacheReadInputTokens"] = 80
usage["cacheWriteInputTokens"] = 20

return {
"output": {"message": {"role": "assistant", "content": [{"text": "Hello"}]}},
"stopReason": "end_turn",
"usage": {"inputTokens": 50, "outputTokens": 30, "totalTokens": 80},
"usage": usage,

Check warning on line 33 in tests/integrations/boto3/test_bedrock.py

View check run for this annotation

@sentry/warden / warden: code-review

[BXV-G3H] Total token attribute omits cache tokens included in input usage (additional location)

In `get_response_attributes`, `GEN_AI_USAGE_TOTAL_TOKENS` is copied from `usage["totalTokens"]` before cached and cache-write tokens are added to `input_tokens`. The resulting total can therefore disagree with the reported input and output tokens; set it to the normalized `input_tokens + output_tokens` after cache-token adjustments.
"metrics": {"latencyMs": 500},
}

Expand Down Expand Up @@ -87,6 +92,15 @@
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_MAX_TOKENS] == 1024
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_TEMPERATURE] == 0.7
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9

assert span["attributes"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 20
assert span["attributes"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 30
if BOTOCORE_VERSION < Version("1.37.24"):
assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 10
return
assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 110
assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] == 80
assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE] == 20
else:
events = capture_events()

Expand Down Expand Up @@ -116,6 +130,15 @@
assert span["data"][SPANDATA.GEN_AI_REQUEST_TEMPERATURE] == 0.7
assert span["data"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9

assert span["data"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 20
assert span["data"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 30
if BOTOCORE_VERSION < Version("1.37.24"):
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 10
return
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 110
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] == 80
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE] == 20


@pytest.mark.parametrize("span_streaming", [True, False])
@pytest.mark.parametrize("stream_gen_ai_spans", [True, False])
Expand Down
Loading
Loading