Skip to content

Commit 6830d12

Browse files
Truncate single messages based on characters
1 parent fc33978 commit 6830d12

1 file changed

Lines changed: 34 additions & 23 deletions

File tree

sentry_sdk/ai/utils.py

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
from copy import deepcopy
23
from collections import deque
34
from typing import TYPE_CHECKING
45
from sys import getsizeof
@@ -12,6 +13,8 @@
1213
from sentry_sdk.utils import logger
1314

1415
MAX_GEN_AI_MESSAGE_BYTES = 20_000 # 20KB
16+
# Maximum characters when only a single message is left after bytes truncation
17+
MAX_SINGLE_MESSAGE_CONTENT_CHARS = 10_000
1518

1619

1720
class GEN_AI_ALLOWED_MESSAGE_ROLES:
@@ -101,27 +104,20 @@ def get_start_span_function():
101104
return sentry_sdk.start_span if transaction_exists else sentry_sdk.start_transaction
102105

103106

104-
def _truncate_single_message(message, max_bytes):
107+
def _truncate_single_message_content_if_present(message, max_chars):
105108
# type: (Dict[str, Any], int) -> Dict[str, Any]
106109
"""
107-
Truncate a single message to fit within max_bytes.
110+
Truncate a single message to fit within max_chars.
108111
If the message is too large, truncate the content field.
109112
"""
110113
if not isinstance(message, dict) or "content" not in message:
111114
return message
112-
content = message.get("content", "")
115+
content = message["content"]
113116

114-
if not isinstance(content, str) or len(content) <= max_bytes:
117+
if not isinstance(content, str) or len(content) <= max_chars:
115118
return message
116119

117-
overhead_message = message.copy()
118-
overhead_message["content"] = ""
119-
overhead_size = len(
120-
json.dumps(overhead_message, separators=(",", ":")).encode("utf-8")
121-
)
122-
123-
available_content_bytes = max_bytes - overhead_size - 20
124-
message["content"] = content[:available_content_bytes] + "..."
120+
message["content"] = content[:max_chars] + "..."
125121
return message
126122

127123

@@ -142,22 +138,37 @@ def _find_truncation_index(messages, max_bytes):
142138
return 0
143139

144140

145-
def truncate_messages_by_size(messages, max_bytes=MAX_GEN_AI_MESSAGE_BYTES):
146-
# type: (List[Dict[str, Any]], int) -> Tuple[List[Dict[str, Any]], int]
147-
messages_with_truncated_content = [
148-
_truncate_single_message(msg, max_bytes) for msg in messages
149-
]
150-
151-
serialized_json = json.dumps(messages_with_truncated_content, separators=(",", ":"))
141+
def truncate_messages_by_size(
142+
messages,
143+
max_bytes=MAX_GEN_AI_MESSAGE_BYTES,
144+
max_single_message_chars=MAX_SINGLE_MESSAGE_CONTENT_CHARS,
145+
):
146+
# type: (List[Dict[str, Any]], int, int) -> Tuple[List[Dict[str, Any]], int]
147+
"""
148+
Returns a truncated messages array, consisting of
149+
- the last message, with the messages's content truncated to `max_single_message_chars` characters,
150+
if the last message's size exceeds `max_bytes`; otherwise,
151+
- the maximum number of messages, starting from the end of the `messages` array, whose total
152+
serialized size does not exceed `max_bytes` bytes.
153+
"""
154+
serialized_json = json.dumps(messages, separators=(",", ":"))
152155
current_size = len(serialized_json.encode("utf-8"))
153156

154157
if current_size <= max_bytes:
155-
return messages_with_truncated_content, 0
158+
return messages, 0
156159

157-
truncation_index = _find_truncation_index(
158-
messages_with_truncated_content, max_bytes
160+
truncation_index = _find_truncation_index(messages, max_bytes)
161+
truncated_messages = (
162+
messages[truncation_index:]
163+
if truncation_index < len(messages)
164+
else messages[-1:]
159165
)
160-
return messages_with_truncated_content[truncation_index:], truncation_index
166+
if len(truncated_messages) == 1:
167+
truncated_messages[0] = _truncate_single_message_content_if_present(
168+
deepcopy(truncated_messages[0]), max_chars=max_single_message_chars
169+
)
170+
171+
return truncated_messages, truncation_index
161172

162173

163174
def truncate_and_annotate_messages(

0 commit comments

Comments
 (0)