Skip to content

Commit 44ec306

Browse files
committed
wip
1 parent da8813b commit 44ec306

File tree

13 files changed

+148
-97
lines changed

13 files changed

+148
-97
lines changed

sentry_sdk/ai/utils.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,22 @@ def truncate_messages_by_size(messages, max_bytes=MAX_GEN_AI_MESSAGE_BYTES):
127127
truncated_messages.pop(0)
128128

129129
if len(truncated_messages) == 1:
130-
serialized = serialize(
131-
truncated_messages, is_vars=False, max_value_length=round(max_bytes * 0.8)
132-
)
133-
serialized_json = json.dumps(serialized, separators=(",", ":"))
134-
current_size = len(serialized_json.encode("utf-8"))
130+
last_message = truncated_messages[0].copy()
131+
content = last_message.get("content", "")
135132

136-
if current_size > max_bytes:
137-
last_message = truncated_messages[0].copy()
138-
content = last_message.get("content", "")
133+
if content and isinstance(content, str):
134+
if len(content) > int(max_bytes * 0.8):
135+
last_message["content"] = content[: int(max_bytes * 0.8)] + "..."
136+
else:
137+
last_message["content"] = content
138+
truncated_messages[0] = last_message
139139

140-
if content and isinstance(content, str):
140+
if content and isinstance(content, list):
141+
if len(content) > int(max_bytes * 0.8):
141142
last_message["content"] = content[: int(max_bytes * 0.8)] + "..."
142-
truncated_messages[0] = last_message
143+
else:
144+
last_message["content"] = content
145+
truncated_messages[0] = last_message
143146

144147
return truncated_messages
145148

sentry_sdk/integrations/anthropic.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,9 @@ def _set_input_data(span, kwargs, integration):
151151
role_normalized_messages, span, scope
152152
)
153153
if messages_data is not None:
154-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
154+
set_data_normalized(
155+
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
156+
)
155157

156158
set_data_normalized(
157159
span, SPANDATA.GEN_AI_RESPONSE_STREAMING, kwargs.get("stream", False)

sentry_sdk/integrations/langchain.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,12 @@ def on_llm_start(
227227
normalized_messages, span, scope
228228
)
229229
if messages_data is not None:
230-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
230+
set_data_normalized(
231+
span,
232+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
233+
messages_data,
234+
unpack=False,
235+
)
231236

232237
def on_chat_model_start(self, serialized, messages, *, run_id, **kwargs):
233238
# type: (SentryLangchainCallback, Dict[str, Any], List[List[BaseMessage]], UUID, Any) -> Any
@@ -284,7 +289,12 @@ def on_chat_model_start(self, serialized, messages, *, run_id, **kwargs):
284289
normalized_messages, span, scope
285290
)
286291
if messages_data is not None:
287-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
292+
set_data_normalized(
293+
span,
294+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
295+
messages_data,
296+
unpack=False,
297+
)
288298

289299
def on_chat_model_end(self, response, *, run_id, **kwargs):
290300
# type: (SentryLangchainCallback, LLMResult, UUID, Any) -> Any
@@ -763,7 +773,12 @@ def new_invoke(self, *args, **kwargs):
763773
normalized_messages, span, scope
764774
)
765775
if messages_data is not None:
766-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
776+
set_data_normalized(
777+
span,
778+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
779+
messages_data,
780+
unpack=False,
781+
)
767782

768783
output = result.get("output")
769784
if (
@@ -817,7 +832,9 @@ def new_stream(self, *args, **kwargs):
817832
normalized_messages, span, sentry_sdk.get_current_scope()
818833
)
819834
if messages_data is not None:
820-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
835+
set_data_normalized(
836+
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
837+
)
821838

822839
# Run the agent
823840
result = f(self, *args, **kwargs)

sentry_sdk/integrations/langgraph.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,12 @@ def new_invoke(self, *args, **kwargs):
190190
normalized_input_messages, span, scope
191191
)
192192
if messages_data is not None:
193-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
193+
set_data_normalized(
194+
span,
195+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
196+
messages_data,
197+
unpack=False,
198+
)
194199

195200
result = f(self, *args, **kwargs)
196201

@@ -241,7 +246,12 @@ async def new_ainvoke(self, *args, **kwargs):
241246
normalized_input_messages, span, scope
242247
)
243248
if messages_data is not None:
244-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
249+
set_data_normalized(
250+
span,
251+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
252+
messages_data,
253+
unpack=False,
254+
)
245255

246256
result = await f(self, *args, **kwargs)
247257

sentry_sdk/integrations/litellm.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ def _input_callback(kwargs):
8181
scope = sentry_sdk.get_current_scope()
8282
messages_data = truncate_and_annotate_messages(normalized_messages, span, scope)
8383
if messages_data is not None:
84-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
84+
set_data_normalized(
85+
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
86+
)
8587

8688
# Record other parameters
8789
params = {

sentry_sdk/integrations/openai.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ def _set_input_data(span, kwargs, operation, integration):
190190
scope = sentry_sdk.get_current_scope()
191191
messages_data = truncate_and_annotate_messages(normalized_messages, span, scope)
192192
if messages_data is not None:
193-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
193+
set_data_normalized(
194+
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
195+
)
194196

195197
# Input attributes: Common
196198
set_data_normalized(span, SPANDATA.GEN_AI_SYSTEM, "openai")

sentry_sdk/integrations/openai_agents/spans/invoke_agent.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ def invoke_agent_span(context, agent, kwargs):
6767
normalized_messages, span, scope
6868
)
6969
if messages_data is not None:
70-
span.set_data(SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data)
70+
set_data_normalized(
71+
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
72+
)
7173

7274
_set_agent_data(span, agent)
7375

tests/integrations/anthropic/test_anthropic.py

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,10 @@ def test_nonstreaming_create_message(
124124
assert span["data"][SPANDATA.GEN_AI_REQUEST_MODEL] == "model"
125125

126126
if send_default_pii and include_prompts:
127-
assert span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] == [
128-
{"role": "user", "content": "Hello, Claude"}
129-
]
127+
assert (
128+
span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
129+
== '[{"role": "user", "content": "Hello, Claude"}]'
130+
)
130131
assert span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT] == "Hi, I'm Claude."
131132
else:
132133
assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"]
@@ -192,9 +193,10 @@ async def test_nonstreaming_create_message_async(
192193
assert span["data"][SPANDATA.GEN_AI_REQUEST_MODEL] == "model"
193194

194195
if send_default_pii and include_prompts:
195-
assert span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] == [
196-
{"role": "user", "content": "Hello, Claude"}
197-
]
196+
assert (
197+
span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
198+
== '[{"role": "user", "content": "Hello, Claude"}]'
199+
)
198200
assert span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT] == "Hi, I'm Claude."
199201
else:
200202
assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"]
@@ -291,9 +293,10 @@ def test_streaming_create_message(
291293
assert span["data"][SPANDATA.GEN_AI_REQUEST_MODEL] == "model"
292294

293295
if send_default_pii and include_prompts:
294-
assert span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] == [
295-
{"role": "user", "content": "Hello, Claude"}
296-
]
296+
assert (
297+
span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
298+
== '[{"role": "user", "content": "Hello, Claude"}]'
299+
)
297300
assert span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT] == "Hi! I'm Claude!"
298301

299302
else:
@@ -394,9 +397,10 @@ async def test_streaming_create_message_async(
394397
assert span["data"][SPANDATA.GEN_AI_REQUEST_MODEL] == "model"
395398

396399
if send_default_pii and include_prompts:
397-
assert span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] == [
398-
{"role": "user", "content": "Hello, Claude"}
399-
]
400+
assert (
401+
span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
402+
== '[{"role": "user", "content": "Hello, Claude"}]'
403+
)
400404
assert span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT] == "Hi! I'm Claude!"
401405

402406
else:
@@ -524,9 +528,10 @@ def test_streaming_create_message_with_input_json_delta(
524528
assert span["data"][SPANDATA.GEN_AI_REQUEST_MODEL] == "model"
525529

526530
if send_default_pii and include_prompts:
527-
assert span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] == [
528-
{"role": "user", "content": "What is the weather like in San Francisco?"}
529-
]
531+
assert (
532+
span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
533+
== '[{"role": "user", "content": "What is the weather like in San Francisco?"}]'
534+
)
530535
assert (
531536
span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT]
532537
== "{'location': 'San Francisco, CA'}"
@@ -663,9 +668,10 @@ async def test_streaming_create_message_with_input_json_delta_async(
663668
assert span["data"][SPANDATA.GEN_AI_REQUEST_MODEL] == "model"
664669

665670
if send_default_pii and include_prompts:
666-
assert span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] == [
667-
{"role": "user", "content": "What is the weather like in San Francisco?"}
668-
]
671+
assert (
672+
span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
673+
== '[{"role": "user", "content": "What is the weather like in San Francisco?"}]'
674+
)
669675
assert (
670676
span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT]
671677
== "{'location': 'San Francisco, CA'}"
@@ -919,7 +925,7 @@ def test_anthropic_message_role_mapping(sentry_init, capture_events):
919925
assert span["op"] == "gen_ai.chat"
920926
assert SPANDATA.GEN_AI_REQUEST_MESSAGES in span["data"]
921927

922-
stored_messages = span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
928+
stored_messages = json.loads(span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES])
923929
assert len(stored_messages) == 4
924930
assert stored_messages[0]["role"] == "system"
925931
assert stored_messages[1]["role"] == "user"
@@ -975,10 +981,13 @@ def test_anthropic_message_truncation(sentry_init, capture_events):
975981

976982
assert SPANDATA.GEN_AI_REQUEST_MESSAGES in span["data"]
977983
messages_data = span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
978-
assert isinstance(messages_data, list)
979-
assert len(messages_data) <= len(large_messages)
984+
assert isinstance(messages_data, str)
985+
986+
parsed_messages = json.loads(messages_data)
987+
assert isinstance(parsed_messages, list)
988+
assert len(parsed_messages) <= len(large_messages)
980989

981-
result_size = len(serialize(messages_data, is_vars=False))
990+
result_size = len(messages_data.encode("utf-8"))
982991
assert result_size <= MAX_GEN_AI_MESSAGE_BYTES
983992

984993

@@ -1021,8 +1030,10 @@ def test_anthropic_single_large_message_preservation(sentry_init, capture_events
10211030

10221031
assert SPANDATA.GEN_AI_REQUEST_MESSAGES in span["data"]
10231032
messages_data = span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
1024-
assert isinstance(messages_data, list)
1033+
assert isinstance(messages_data, str)
10251034

1026-
assert len(messages_data) == 1
1027-
assert messages_data[0]["role"] == "user"
1028-
assert len(messages_data[0]["content"]) < len(huge_content)
1035+
parsed_messages = json.loads(messages_data)
1036+
assert isinstance(parsed_messages, list)
1037+
assert len(parsed_messages) == 1
1038+
assert parsed_messages[0]["role"] == "user"
1039+
assert len(parsed_messages[0]["content"]) < len(huge_content)

tests/integrations/langgraph/test_langgraph.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,9 @@ def __init__(self, content, message_type="human"):
672672

673673
# If messages were captured, verify role mapping
674674
if SPANDATA.GEN_AI_REQUEST_MESSAGES in span["data"]:
675-
stored_messages = span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
675+
import json
676+
677+
stored_messages = json.loads(span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES])
676678

677679
# Find messages with specific content to verify role mapping
678680
ai_message = next(

tests/integrations/litellm/test_litellm.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -637,8 +637,10 @@ def test_litellm_single_large_message_preservation(sentry_init, capture_events):
637637

638638
assert SPANDATA.GEN_AI_REQUEST_MESSAGES in span["data"]
639639
messages_data = span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
640-
assert isinstance(messages_data, list)
640+
assert isinstance(messages_data, str)
641641

642-
assert len(messages_data) == 1
643-
assert messages_data[0]["role"] == "user"
644-
assert len(messages_data[0]["content"]) < len(huge_content)
642+
parsed_messages = json.loads(messages_data)
643+
assert isinstance(parsed_messages, list)
644+
assert len(parsed_messages) == 1
645+
assert parsed_messages[0]["role"] == "user"
646+
assert len(parsed_messages[0]["content"]) < len(huge_content)

0 commit comments

Comments
 (0)