Skip to content

Commit 8084a18

Browse files
committed
Record metrics for AI tokens used
1 parent d1b8dbb commit 8084a18

File tree

3 files changed

+43
-39
lines changed

3 files changed

+43
-39
lines changed

sentry_sdk/integrations/_ai_common.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
from sentry_sdk import metrics
12
from sentry_sdk._types import TYPE_CHECKING
3+
from sentry_sdk.consts import SPANDATA
24

35
if TYPE_CHECKING:
4-
from typing import Any
6+
from typing import Any, Optional
57

68
from sentry_sdk.tracing import Span
79
from sentry_sdk.utils import logger
@@ -30,3 +32,26 @@ def set_data_normalized(span, key, value):
3032
# type: (Span, str, Any) -> None
3133
normalized = _normalize_data(value)
3234
span.set_data(key, normalized)
35+
36+
37+
def record_token_usage(
38+
span, prompt_tokens=None, completion_tokens=None, total_tokens=None
39+
):
40+
# type: (Span, Optional[int], Optional[int], Optional[int]) -> None
41+
if prompt_tokens is not None:
42+
span.set_data(SPANDATA.AI_PROMPT_TOKENS_USED, prompt_tokens)
43+
metrics.incr(SPANDATA.AI_PROMPT_TOKENS_USED, value=prompt_tokens, unit="tokens")
44+
if completion_tokens is not None:
45+
span.set_data(SPANDATA.AI_COMPLETION_TOKENS_USED, completion_tokens)
46+
metrics.incr(
47+
SPANDATA.AI_COMPLETION_TOKENS_USED, value=completion_tokens, unit="tokens"
48+
)
49+
if (
50+
total_tokens is None
51+
and prompt_tokens is not None
52+
and completion_tokens is not None
53+
):
54+
total_tokens = prompt_tokens + completion_tokens
55+
if total_tokens is not None:
56+
span.set_data(SPANDATA.AI_TOTAL_TOKENS_USED, total_tokens)
57+
metrics.incr(SPANDATA.AI_TOTAL_TOKENS_USED, value=total_tokens, unit="tokens")

sentry_sdk/integrations/langchain.py

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import sentry_sdk
55
from sentry_sdk._types import TYPE_CHECKING
66
from sentry_sdk.consts import OP, SPANDATA
7-
from sentry_sdk.integrations._ai_common import set_data_normalized
7+
from sentry_sdk.integrations._ai_common import set_data_normalized, record_token_usage
88
from sentry_sdk.scope import should_send_default_pii
99
from sentry_sdk.tracing import Span
1010

@@ -244,31 +244,18 @@ def on_llm_end(self, response, *, run_id, **kwargs):
244244
)
245245

246246
if token_usage:
247-
span_data.span.set_data(
248-
SPANDATA.AI_PROMPT_TOKENS_USED, token_usage.get("prompt_tokens")
249-
)
250-
span_data.span.set_data(
251-
SPANDATA.AI_COMPLETION_TOKENS_USED,
247+
record_token_usage(
248+
span_data.span,
249+
token_usage.get("prompt_tokens"),
252250
token_usage.get("completion_tokens"),
253-
)
254-
span_data.span.set_data(
255-
SPANDATA.AI_TOTAL_TOKENS_USED, token_usage.get("total_tokens")
251+
token_usage.get("total_tokens"),
256252
)
257253
else:
258-
if span_data.num_completion_tokens:
259-
span_data.span.set_data(
260-
SPANDATA.AI_COMPLETION_TOKENS_USED,
261-
span_data.num_completion_tokens,
262-
)
263-
if span_data.num_prompt_tokens:
264-
span_data.span.set_data(
265-
SPANDATA.AI_PROMPT_TOKENS_USED, span_data.num_prompt_tokens
266-
)
267-
if span_data.num_prompt_tokens and span_data.num_completion_tokens:
268-
span_data.span.set_data(
269-
SPANDATA.AI_TOTAL_TOKENS_USED,
270-
span_data.num_prompt_tokens + span_data.num_completion_tokens,
271-
)
254+
record_token_usage(
255+
span_data.span,
256+
span_data.num_prompt_tokens,
257+
span_data.num_completion_tokens,
258+
)
272259

273260
span_data.span.__exit__(None, None, None)
274261
del self.span_map[run_id]

sentry_sdk/integrations/openai.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from sentry_sdk import consts
44
from sentry_sdk._types import TYPE_CHECKING
55
from sentry_sdk.consts import SPANDATA
6-
from sentry_sdk.integrations._ai_common import set_data_normalized
6+
from sentry_sdk.integrations._ai_common import set_data_normalized, record_token_usage
77

88
if TYPE_CHECKING:
99
from typing import Any, Iterable, List, Optional, Callable, Iterator
@@ -109,15 +109,11 @@ def _calculate_chat_completion_usage(
109109
if hasattr(choice, "message"):
110110
completion_tokens += count_tokens(choice.message)
111111

112-
if total_tokens == 0:
113-
total_tokens = prompt_tokens + completion_tokens
114-
115-
if completion_tokens != 0:
116-
set_data_normalized(span, SPANDATA.AI_COMPLETION_TOKENS_USED, completion_tokens)
117-
if prompt_tokens != 0:
118-
set_data_normalized(span, SPANDATA.AI_PROMPT_TOKENS_USED, prompt_tokens)
119-
if total_tokens != 0:
120-
set_data_normalized(span, SPANDATA.AI_TOTAL_TOKENS_USED, total_tokens)
112+
if prompt_tokens == 0:
113+
prompt_tokens = None
114+
if completion_tokens == 0:
115+
completion_tokens = None
116+
record_token_usage(span, prompt_tokens, completion_tokens, total_tokens)
121117

122118

123119
def _wrap_chat_completion_create(f):
@@ -262,11 +258,7 @@ def new_embeddings_create(*args, **kwargs):
262258
if prompt_tokens == 0:
263259
prompt_tokens = count_tokens(kwargs["input"] or "")
264260

265-
if total_tokens == 0:
266-
total_tokens = prompt_tokens
267-
268-
set_data_normalized(span, SPANDATA.AI_PROMPT_TOKENS_USED, prompt_tokens)
269-
set_data_normalized(span, SPANDATA.AI_TOTAL_TOKENS_USED, total_tokens)
261+
record_token_usage(span, prompt_tokens, None, total_tokens or prompt_tokens)
270262

271263
return response
272264

0 commit comments

Comments
 (0)