@@ -899,7 +899,25 @@ def test_set_output_data_with_input_json_delta(sentry_init):
899899 assert span ._data .get (SPANDATA .GEN_AI_USAGE_TOTAL_TOKENS ) == 30
900900
901901
902- def test_anthropic_message_role_mapping (sentry_init , capture_events ):
902+ # Test messages with mixed roles including "ai" that should be mapped to "assistant"
903+ @pytest .mark .parametrize (
904+ "test_message,expected_role" ,
905+ [
906+ ({"role" : "system" , "content" : "You are helpful." }, "system" ),
907+ ({"role" : "user" , "content" : "Hello" }, "user" ),
908+ (
909+ {"role" : "ai" , "content" : "Hi there!" },
910+ "assistant" ,
911+ ), # Should be mapped to "assistant"
912+ (
913+ {"role" : "assistant" , "content" : "How can I help?" },
914+ "assistant" ,
915+ ), # Should stay "assistant"
916+ ],
917+ )
918+ def test_anthropic_message_role_mapping (
919+ sentry_init , capture_events , test_message , expected_role
920+ ):
903921 """Test that Anthropic integration properly maps message roles like 'ai' to 'assistant'"""
904922 sentry_init (
905923 integrations = [AnthropicIntegration (include_prompts = True )],
@@ -924,13 +942,7 @@ def mock_messages_create(*args, **kwargs):
924942
925943 client .messages ._post = mock .Mock (return_value = mock_messages_create ())
926944
927- # Test messages with mixed roles including "ai" that should be mapped to "assistant"
928- test_messages = [
929- {"role" : "system" , "content" : "You are helpful." },
930- {"role" : "user" , "content" : "Hello" },
931- {"role" : "ai" , "content" : "Hi there!" }, # Should be mapped to "assistant"
932- {"role" : "assistant" , "content" : "How can I help?" }, # Should stay "assistant"
933- ]
945+ test_messages = [test_message ]
934946
935947 with start_transaction (name = "anthropic tx" ):
936948 client .messages .create (
@@ -948,22 +960,7 @@ def mock_messages_create(*args, **kwargs):
948960 # Parse the stored messages
949961 stored_messages = json .loads (span ["data" ][SPANDATA .GEN_AI_REQUEST_MESSAGES ])
950962
951- # Verify that "ai" role was mapped to "assistant"
952- assert len (stored_messages ) == 4
953- assert stored_messages [0 ]["role" ] == "system"
954- assert stored_messages [1 ]["role" ] == "user"
955- assert (
956- stored_messages [2 ]["role" ] == "assistant"
957- ) # "ai" should be mapped to "assistant"
958- assert stored_messages [3 ]["role" ] == "assistant" # should stay "assistant"
959-
960- # Verify content is preserved
961- assert stored_messages [2 ]["content" ] == "Hi there!"
962- assert stored_messages [3 ]["content" ] == "How can I help?"
963-
964- # Verify no "ai" roles remain
965- roles = [msg ["role" ] for msg in stored_messages ]
966- assert "ai" not in roles
963+ assert stored_messages [0 ]["role" ] == expected_role
967964
968965
969966def test_anthropic_message_truncation (sentry_init , capture_events ):
@@ -1010,9 +1007,60 @@ def test_anthropic_message_truncation(sentry_init, capture_events):
10101007
10111008 parsed_messages = json .loads (messages_data )
10121009 assert isinstance (parsed_messages , list )
1013- assert len (parsed_messages ) == 2
1014- assert "small message 4" in str (parsed_messages [0 ])
1015- assert "small message 5" in str (parsed_messages [1 ])
1010+ assert len (parsed_messages ) == 1
1011+ assert "small message 5" in str (parsed_messages [0 ])
1012+
1013+ assert tx ["_meta" ]["spans" ]["0" ]["data" ]["gen_ai.request.messages" ]["" ]["len" ] == 5
1014+
1015+
1016+ @pytest .mark .asyncio
1017+ async def test_anthropic_message_truncation_async (sentry_init , capture_events ):
1018+ """Test that large messages are truncated properly in Anthropic integration."""
1019+ sentry_init (
1020+ integrations = [AnthropicIntegration (include_prompts = True )],
1021+ traces_sample_rate = 1.0 ,
1022+ send_default_pii = True ,
1023+ )
1024+ events = capture_events ()
1025+
1026+ client = AsyncAnthropic (api_key = "z" )
1027+ client .messages ._post = mock .AsyncMock (return_value = EXAMPLE_MESSAGE )
1028+
1029+ large_content = (
1030+ "This is a very long message that will exceed our size limits. " * 1000
1031+ )
1032+ messages = [
1033+ {"role" : "user" , "content" : "small message 1" },
1034+ {"role" : "assistant" , "content" : large_content },
1035+ {"role" : "user" , "content" : large_content },
1036+ {"role" : "assistant" , "content" : "small message 4" },
1037+ {"role" : "user" , "content" : "small message 5" },
1038+ ]
1039+
1040+ with start_transaction ():
1041+ await client .messages .create (max_tokens = 1024 , messages = messages , model = "model" )
1042+
1043+ assert len (events ) > 0
1044+ tx = events [0 ]
1045+ assert tx ["type" ] == "transaction"
1046+
1047+ chat_spans = [
1048+ span for span in tx .get ("spans" , []) if span .get ("op" ) == OP .GEN_AI_CHAT
1049+ ]
1050+ assert len (chat_spans ) > 0
1051+
1052+ chat_span = chat_spans [0 ]
1053+ assert chat_span ["data" ][SPANDATA .GEN_AI_OPERATION_NAME ] == "chat"
1054+ assert SPANDATA .GEN_AI_REQUEST_MESSAGES in chat_span ["data" ]
1055+
1056+ messages_data = chat_span ["data" ][SPANDATA .GEN_AI_REQUEST_MESSAGES ]
1057+ assert isinstance (messages_data , str )
1058+
1059+ parsed_messages = json .loads (messages_data )
1060+ assert isinstance (parsed_messages , list )
1061+ assert len (parsed_messages ) == 1
1062+ assert "small message 5" in str (parsed_messages [0 ])
1063+
10161064 assert tx ["_meta" ]["spans" ]["0" ]["data" ]["gen_ai.request.messages" ]["" ]["len" ] == 5
10171065
10181066
0 commit comments