@@ -850,8 +850,10 @@ def test_collect_ai_data_with_input_json_delta():
850850 output_tokens = 20
851851 content_blocks = []
852852
853- model , new_input_tokens , new_output_tokens , new_content_blocks = _collect_ai_data (
854- event , model , input_tokens , output_tokens , content_blocks
853+ model , new_input_tokens , new_output_tokens , _ , _ , new_content_blocks = (
854+ _collect_ai_data (
855+ event , model , input_tokens , output_tokens , 0 , 0 , content_blocks
856+ )
855857 )
856858
857859 assert model is None
@@ -881,6 +883,8 @@ def test_set_output_data_with_input_json_delta(sentry_init):
881883 model = "" ,
882884 input_tokens = 10 ,
883885 output_tokens = 20 ,
886+ cache_read_input_tokens = 0 ,
887+ cache_write_input_tokens = 0 ,
884888 content_blocks = [{"text" : "" .join (json_deltas ), "type" : "text" }],
885889 )
886890
@@ -1449,118 +1453,44 @@ def test_system_prompt_with_complex_structure(sentry_init, capture_events):
14491453
14501454
14511455def test_cache_tokens_nonstreaming (sentry_init , capture_events ):
1452- """Test that cache read and write tokens are properly tracked for non-streaming responses."""
1453- sentry_init (
1454- integrations = [AnthropicIntegration (include_prompts = True )],
1455- traces_sample_rate = 1.0 ,
1456- send_default_pii = True ,
1457- )
1456+ """Test cache read/write tokens are tracked for non-streaming responses."""
1457+ sentry_init (integrations = [AnthropicIntegration ()], traces_sample_rate = 1.0 )
14581458 events = capture_events ()
14591459 client = Anthropic (api_key = "z" )
14601460
1461- # Create a message with cache token usage
1462- message_with_cache = Message (
1463- id = "id" ,
1464- model = "claude-3-5-sonnet-20241022" ,
1465- role = "assistant" ,
1466- content = [TextBlock (type = "text" , text = "Response using cache" )],
1467- type = "message" ,
1468- usage = Usage (
1469- input_tokens = 100 ,
1470- output_tokens = 50 ,
1471- cache_read_input_tokens = 80 , # 80 tokens read from cache
1472- cache_write_input_tokens = 20 , # 20 tokens written to cache
1473- ),
1474- )
1475-
1476- client .messages ._post = mock .Mock (return_value = message_with_cache )
1477-
1478- messages = [{"role" : "user" , "content" : "Hello" }]
1479-
1480- with start_transaction (name = "anthropic" ):
1481- response = client .messages .create (
1482- max_tokens = 1024 , messages = messages , model = "claude-3-5-sonnet-20241022"
1461+ client .messages ._post = mock .Mock (
1462+ return_value = Message (
1463+ id = "id" ,
1464+ model = "claude-3-5-sonnet-20241022" ,
1465+ role = "assistant" ,
1466+ content = [TextBlock (type = "text" , text = "Response" )],
1467+ type = "message" ,
1468+ usage = Usage (
1469+ input_tokens = 100 ,
1470+ output_tokens = 50 ,
1471+ cache_read_input_tokens = 80 ,
1472+ cache_creation_input_tokens = 20 ,
1473+ ),
14831474 )
1484-
1485- assert response == message_with_cache
1486- usage = response .usage
1487-
1488- assert usage .input_tokens == 100
1489- assert usage .output_tokens == 50
1490- assert usage .cache_read_input_tokens == 80
1491- assert usage .cache_write_input_tokens == 20
1492-
1493- assert len (events ) == 1
1494- (event ,) = events
1495-
1496- assert event ["type" ] == "transaction"
1497- assert len (event ["spans" ]) == 1
1498- (span ,) = event ["spans" ]
1499-
1500- assert span ["op" ] == OP .GEN_AI_CHAT
1501- assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS ] == 100
1502- assert span ["data" ][SPANDATA .GEN_AI_USAGE_OUTPUT_TOKENS ] == 50
1503- assert span ["data" ][SPANDATA .GEN_AI_USAGE_TOTAL_TOKENS ] == 150
1504- # Check cache-related tokens
1505- assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHED ] == 80
1506- assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE ] == 20
1507-
1508-
1509- def test_cache_tokens_only_reads (sentry_init , capture_events ):
1510- """Test tracking when only cache reads are present (no writes)."""
1511- sentry_init (
1512- integrations = [AnthropicIntegration (include_prompts = True )],
1513- traces_sample_rate = 1.0 ,
1514- send_default_pii = True ,
1515- )
1516- events = capture_events ()
1517- client = Anthropic (api_key = "z" )
1518-
1519- # Message with only cache reads, no writes
1520- message_cache_read_only = Message (
1521- id = "id" ,
1522- model = "claude-3-5-sonnet-20241022" ,
1523- role = "assistant" ,
1524- content = [TextBlock (type = "text" , text = "Response" )],
1525- type = "message" ,
1526- usage = Usage (
1527- input_tokens = 100 ,
1528- output_tokens = 50 ,
1529- cache_read_input_tokens = 100 , # All tokens read from cache
1530- cache_write_input_tokens = 0 , # No new cache writes
1531- ),
15321475 )
15331476
1534- client .messages ._post = mock .Mock (return_value = message_cache_read_only )
1535-
15361477 with start_transaction (name = "anthropic" ):
15371478 client .messages .create (
15381479 max_tokens = 1024 ,
15391480 messages = [{"role" : "user" , "content" : "Hello" }],
15401481 model = "claude-3-5-sonnet-20241022" ,
15411482 )
15421483
1543- assert len (events ) == 1
1544- (event ,) = events
1545- (span ,) = event ["spans" ]
1546-
1547- assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHED ] == 100
1548- # Cache write should not be present when it's 0
1549- assert SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE not in span ["data" ]
1484+ (span ,) = events [0 ]["spans" ]
1485+ assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHED ] == 80
1486+ assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE ] == 20
15501487
15511488
15521489def test_cache_tokens_streaming (sentry_init , capture_events ):
1553- """Test that cache tokens are tracked correctly for streaming responses."""
1554- sentry_init (
1555- integrations = [AnthropicIntegration (include_prompts = True )],
1556- traces_sample_rate = 1.0 ,
1557- send_default_pii = True ,
1558- )
1559- events = capture_events ()
1490+ """Test cache tokens are tracked for streaming responses."""
15601491 client = Anthropic (api_key = "z" )
1561-
1562- # Create streaming events with cache usage
1563- stream_events = [
1492+ returned_stream = Stream (cast_to = None , response = None , client = client )
1493+ returned_stream ._iterator = [
15641494 MessageStartEvent (
15651495 type = "message_start" ,
15661496 message = Message (
@@ -1573,162 +1503,30 @@ def test_cache_tokens_streaming(sentry_init, capture_events):
15731503 input_tokens = 100 ,
15741504 output_tokens = 0 ,
15751505 cache_read_input_tokens = 80 ,
1576- cache_write_input_tokens = 20 ,
1506+ cache_creation_input_tokens = 20 ,
15771507 ),
15781508 ),
15791509 ),
1580- ContentBlockDeltaEvent (
1581- type = "content_block_delta" ,
1582- index = 0 ,
1583- delta = TextDelta (type = "text_delta" , text = "Hello" ),
1584- ),
15851510 MessageDeltaEvent (
15861511 type = "message_delta" ,
15871512 delta = Delta (stop_reason = "end_turn" ),
15881513 usage = MessageDeltaUsage (output_tokens = 10 ),
15891514 ),
15901515 ]
15911516
1592- mock_stream = mock .MagicMock (spec = Stream )
1593- mock_stream .__iter__ = mock .Mock (return_value = iter (stream_events ))
1594- mock_stream ._iterator = iter (stream_events )
1595-
1596- client .messages ._post = mock .Mock (return_value = mock_stream )
1597-
1598- with start_transaction (name = "anthropic" ):
1599- stream = client .messages .create (
1600- max_tokens = 1024 ,
1601- messages = [{"role" : "user" , "content" : "Hello" }],
1602- model = "claude-3-5-sonnet-20241022" ,
1603- stream = True ,
1604- )
1605- # Consume the stream
1606- for _ in stream :
1607- pass
1608-
1609- assert len (events ) == 1
1610- (event ,) = events
1611- (span ,) = event ["spans" ]
1612-
1613- assert span ["op" ] == OP .GEN_AI_CHAT
1614- assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS ] == 100
1615- assert span ["data" ][SPANDATA .GEN_AI_USAGE_OUTPUT_TOKENS ] == 10
1616- assert span ["data" ][SPANDATA .GEN_AI_USAGE_TOTAL_TOKENS ] == 110
1617- # Check streaming cache tokens
1618- assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHED ] == 80
1619- assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE ] == 20
1620- assert span ["data" ][SPANDATA .GEN_AI_RESPONSE_STREAMING ] is True
1621-
1622-
1623- @pytest .mark .asyncio
1624- async def test_cache_tokens_streaming_async (sentry_init , capture_events ):
1625- """Test that cache tokens are tracked correctly for async streaming responses."""
1626- sentry_init (
1627- integrations = [AnthropicIntegration (include_prompts = True )],
1628- traces_sample_rate = 1.0 ,
1629- send_default_pii = True ,
1630- )
1517+ sentry_init (integrations = [AnthropicIntegration ()], traces_sample_rate = 1.0 )
16311518 events = capture_events ()
1632- client = AsyncAnthropic (api_key = "z" )
1633-
1634- async def async_iterator (values ):
1635- for value in values :
1636- yield value
1637-
1638- # Create streaming events with cache usage
1639- stream_events = [
1640- MessageStartEvent (
1641- type = "message_start" ,
1642- message = Message (
1643- id = "id" ,
1644- model = "claude-3-5-sonnet-20241022" ,
1645- role = "assistant" ,
1646- content = [],
1647- type = "message" ,
1648- usage = Usage (
1649- input_tokens = 100 ,
1650- output_tokens = 0 ,
1651- cache_read_input_tokens = 80 ,
1652- cache_write_input_tokens = 20 ,
1653- ),
1654- ),
1655- ),
1656- ContentBlockDeltaEvent (
1657- type = "content_block_delta" ,
1658- index = 0 ,
1659- delta = TextDelta (type = "text_delta" , text = "Hello" ),
1660- ),
1661- MessageDeltaEvent (
1662- type = "message_delta" ,
1663- delta = Delta (stop_reason = "end_turn" ),
1664- usage = MessageDeltaUsage (output_tokens = 10 ),
1665- ),
1666- ]
1667-
1668- mock_stream = mock .MagicMock (spec = AsyncStream )
1669- mock_stream .__aiter__ = mock .Mock (return_value = async_iterator (stream_events ))
1670- mock_stream ._iterator = async_iterator (stream_events )
1671-
1672- client .messages ._post = mock .Mock (return_value = mock_stream )
1519+ client .messages ._post = mock .Mock (return_value = returned_stream )
16731520
16741521 with start_transaction (name = "anthropic" ):
1675- stream = await client .messages .create (
1522+ for _ in client .messages .create (
16761523 max_tokens = 1024 ,
16771524 messages = [{"role" : "user" , "content" : "Hello" }],
16781525 model = "claude-3-5-sonnet-20241022" ,
16791526 stream = True ,
1680- )
1681- # Consume the stream
1682- async for _ in stream :
1527+ ):
16831528 pass
16841529
1685- assert len (events ) == 1
1686- (event ,) = events
1687- (span ,) = event ["spans" ]
1688-
1689- assert span ["op" ] == OP .GEN_AI_CHAT
1690- assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS ] == 100
1691- assert span ["data" ][SPANDATA .GEN_AI_USAGE_OUTPUT_TOKENS ] == 10
1692- # Check async streaming cache tokens
1530+ (span ,) = events [0 ]["spans" ]
16931531 assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHED ] == 80
16941532 assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE ] == 20
1695-
1696-
1697- def test_no_cache_tokens (sentry_init , capture_events ):
1698- """Test that requests without cache usage don't have cache fields."""
1699- sentry_init (
1700- integrations = [AnthropicIntegration (include_prompts = True )],
1701- traces_sample_rate = 1.0 ,
1702- send_default_pii = True ,
1703- )
1704- events = capture_events ()
1705- client = Anthropic (api_key = "z" )
1706-
1707- # Message without any cache usage
1708- message_no_cache = Message (
1709- id = "id" ,
1710- model = "claude-3-5-sonnet-20241022" ,
1711- role = "assistant" ,
1712- content = [TextBlock (type = "text" , text = "Response" )],
1713- type = "message" ,
1714- usage = Usage (input_tokens = 100 , output_tokens = 50 ),
1715- )
1716-
1717- client .messages ._post = mock .Mock (return_value = message_no_cache )
1718-
1719- with start_transaction (name = "anthropic" ):
1720- client .messages .create (
1721- max_tokens = 1024 ,
1722- messages = [{"role" : "user" , "content" : "Hello" }],
1723- model = "claude-3-5-sonnet-20241022" ,
1724- )
1725-
1726- assert len (events ) == 1
1727- (event ,) = events
1728- (span ,) = event ["spans" ]
1729-
1730- assert span ["data" ][SPANDATA .GEN_AI_USAGE_INPUT_TOKENS ] == 100
1731- assert span ["data" ][SPANDATA .GEN_AI_USAGE_OUTPUT_TOKENS ] == 50
1732- # Cache fields should not be present
1733- assert SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHED not in span ["data" ]
1734- assert SPANDATA .GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE not in span ["data" ]
0 commit comments