@@ -75,29 +75,47 @@ def _capture_exception(exc: "Any") -> None:
7575 sentry_sdk .capture_event (event , hint = hint )
7676
7777
78- def _get_token_usage (result : "Messages" ) -> "tuple[int, int]" :
78+ def _get_token_usage (result : "Messages" ) -> "tuple[int, int, int, int ]" :
7979 """
8080 Get token usage from the Anthropic response.
81+ Returns: (input_tokens, output_tokens, cache_read_input_tokens, cache_write_input_tokens)
8182 """
8283 input_tokens = 0
8384 output_tokens = 0
85+ cache_read_input_tokens = 0
86+ cache_write_input_tokens = 0
8487 if hasattr (result , "usage" ):
8588 usage = result .usage
8689 if hasattr (usage , "input_tokens" ) and isinstance (usage .input_tokens , int ):
8790 input_tokens = usage .input_tokens
8891 if hasattr (usage , "output_tokens" ) and isinstance (usage .output_tokens , int ):
8992 output_tokens = usage .output_tokens
90-
91- return input_tokens , output_tokens
93+ if hasattr (usage , "cache_read_input_tokens" ) and isinstance (
94+ usage .cache_read_input_tokens , int
95+ ):
96+ cache_read_input_tokens = usage .cache_read_input_tokens
97+ if hasattr (usage , "cache_creation_input_tokens" ) and isinstance (
98+ usage .cache_creation_input_tokens , int
99+ ):
100+ cache_write_input_tokens = usage .cache_creation_input_tokens
101+
102+ return (
103+ input_tokens ,
104+ output_tokens ,
105+ cache_read_input_tokens ,
106+ cache_write_input_tokens ,
107+ )
92108
93109
94110def _collect_ai_data (
95111 event : "MessageStreamEvent" ,
96112 model : "str | None" ,
97113 input_tokens : int ,
98114 output_tokens : int ,
115+ cache_read_input_tokens : int ,
116+ cache_write_input_tokens : int ,
99117 content_blocks : "list[str]" ,
100- ) -> "tuple[str | None, int, int, list[str]]" :
118+ ) -> "tuple[str | None, int, int, int, int, list[str]]" :
101119 """
102120 Collect model information, token usage, and collect content blocks from the AI streaming response.
103121 """
@@ -107,6 +125,14 @@ def _collect_ai_data(
107125 usage = event .message .usage
108126 input_tokens += usage .input_tokens
109127 output_tokens += usage .output_tokens
128+ if hasattr (usage , "cache_read_input_tokens" ) and isinstance (
129+ usage .cache_read_input_tokens , int
130+ ):
131+ cache_read_input_tokens += usage .cache_read_input_tokens
132+ if hasattr (usage , "cache_creation_input_tokens" ) and isinstance (
133+ usage .cache_creation_input_tokens , int
134+ ):
135+ cache_write_input_tokens += usage .cache_creation_input_tokens
110136 model = event .message .model or model
111137 elif event .type == "content_block_start" :
112138 pass
@@ -120,7 +146,14 @@ def _collect_ai_data(
120146 elif event .type == "message_delta" :
121147 output_tokens += event .usage .output_tokens
122148
123- return model , input_tokens , output_tokens , content_blocks
149+ return (
150+ model ,
151+ input_tokens ,
152+ output_tokens ,
153+ cache_read_input_tokens ,
154+ cache_write_input_tokens ,
155+ content_blocks ,
156+ )
124157
125158
126159def _transform_anthropic_content_block (
@@ -265,6 +298,8 @@ def _set_output_data(
265298 model : "str | None" ,
266299 input_tokens : "int | None" ,
267300 output_tokens : "int | None" ,
301+ cache_read_input_tokens : "int | None" ,
302+ cache_write_input_tokens : "int | None" ,
268303 content_blocks : "list[Any]" ,
269304 finish_span : bool = False ,
270305) -> None :
@@ -300,6 +335,8 @@ def _set_output_data(
300335 span ,
301336 input_tokens = input_tokens ,
302337 output_tokens = output_tokens ,
338+ input_tokens_cached = cache_read_input_tokens ,
339+ input_tokens_cache_write = cache_write_input_tokens ,
303340 )
304341
305342 if finish_span :
@@ -334,7 +371,12 @@ def _sentry_patched_create_common(f: "Any", *args: "Any", **kwargs: "Any") -> "A
334371
335372 with capture_internal_exceptions ():
336373 if hasattr (result , "content" ):
337- input_tokens , output_tokens = _get_token_usage (result )
374+ (
375+ input_tokens ,
376+ output_tokens ,
377+ cache_read_input_tokens ,
378+ cache_write_input_tokens ,
379+ ) = _get_token_usage (result )
338380
339381 content_blocks = []
340382 for content_block in result .content :
@@ -351,6 +393,8 @@ def _sentry_patched_create_common(f: "Any", *args: "Any", **kwargs: "Any") -> "A
351393 model = getattr (result , "model" , None ),
352394 input_tokens = input_tokens ,
353395 output_tokens = output_tokens ,
396+ cache_read_input_tokens = cache_read_input_tokens ,
397+ cache_write_input_tokens = cache_write_input_tokens ,
354398 content_blocks = content_blocks ,
355399 finish_span = True ,
356400 )
@@ -363,13 +407,26 @@ def new_iterator() -> "Iterator[MessageStreamEvent]":
363407 model = None
364408 input_tokens = 0
365409 output_tokens = 0
410+ cache_read_input_tokens = 0
411+ cache_write_input_tokens = 0
366412 content_blocks : "list[str]" = []
367413
368414 for event in old_iterator :
369- model , input_tokens , output_tokens , content_blocks = (
370- _collect_ai_data (
371- event , model , input_tokens , output_tokens , content_blocks
372- )
415+ (
416+ model ,
417+ input_tokens ,
418+ output_tokens ,
419+ cache_read_input_tokens ,
420+ cache_write_input_tokens ,
421+ content_blocks ,
422+ ) = _collect_ai_data (
423+ event ,
424+ model ,
425+ input_tokens ,
426+ output_tokens ,
427+ cache_read_input_tokens ,
428+ cache_write_input_tokens ,
429+ content_blocks ,
373430 )
374431 yield event
375432
@@ -379,6 +436,8 @@ def new_iterator() -> "Iterator[MessageStreamEvent]":
379436 model = model ,
380437 input_tokens = input_tokens ,
381438 output_tokens = output_tokens ,
439+ cache_read_input_tokens = cache_read_input_tokens ,
440+ cache_write_input_tokens = cache_write_input_tokens ,
382441 content_blocks = [{"text" : "" .join (content_blocks ), "type" : "text" }],
383442 finish_span = True ,
384443 )
@@ -387,13 +446,26 @@ async def new_iterator_async() -> "AsyncIterator[MessageStreamEvent]":
387446 model = None
388447 input_tokens = 0
389448 output_tokens = 0
449+ cache_read_input_tokens = 0
450+ cache_write_input_tokens = 0
390451 content_blocks : "list[str]" = []
391452
392453 async for event in old_iterator :
393- model , input_tokens , output_tokens , content_blocks = (
394- _collect_ai_data (
395- event , model , input_tokens , output_tokens , content_blocks
396- )
454+ (
455+ model ,
456+ input_tokens ,
457+ output_tokens ,
458+ cache_read_input_tokens ,
459+ cache_write_input_tokens ,
460+ content_blocks ,
461+ ) = _collect_ai_data (
462+ event ,
463+ model ,
464+ input_tokens ,
465+ output_tokens ,
466+ cache_read_input_tokens ,
467+ cache_write_input_tokens ,
468+ content_blocks ,
397469 )
398470 yield event
399471
@@ -403,6 +475,8 @@ async def new_iterator_async() -> "AsyncIterator[MessageStreamEvent]":
403475 model = model ,
404476 input_tokens = input_tokens ,
405477 output_tokens = output_tokens ,
478+ cache_read_input_tokens = cache_read_input_tokens ,
479+ cache_write_input_tokens = cache_write_input_tokens ,
406480 content_blocks = [{"text" : "" .join (content_blocks ), "type" : "text" }],
407481 finish_span = True ,
408482 )
0 commit comments