@@ -1026,54 +1026,29 @@ def test_langchain_openai_tools_agent(
10261026 assert "get_word_length" in tools_data
10271027
10281028
1029- @pytest .mark .parametrize (
1030- "send_default_pii, include_prompts" ,
1031- [
1032- (True , True ),
1033- (True , False ),
1034- (False , True ),
1035- (False , False ),
1036- ],
1037- )
1038- @pytest .mark .parametrize (
1039- "system_instructions_content" ,
1040- [
1041- "You are very powerful assistant, but don't know current events" ,
1042- ["You are a helpful assistant." , "Be concise and clear." ],
1043- [
1044- {"type" : "text" , "text" : "You are a helpful assistant." },
1045- {"type" : "text" , "text" : "Be concise and clear." },
1046- ],
1047- ],
1048- ids = ["string" , "list" , "blocks" ],
1049- )
10501029def test_langchain_openai_tools_agent_with_config (
10511030 sentry_init ,
10521031 capture_events ,
1053- send_default_pii ,
1054- include_prompts ,
1055- system_instructions_content ,
1056- request ,
10571032 get_model_response ,
10581033 server_side_event_chunks ,
10591034 streaming_chat_completions_model_responses ,
10601035):
10611036 sentry_init (
10621037 integrations = [
10631038 LangchainIntegration (
1064- include_prompts = include_prompts ,
1039+ include_prompts = True ,
10651040 )
10661041 ],
10671042 traces_sample_rate = 1.0 ,
1068- send_default_pii = send_default_pii ,
1043+ send_default_pii = True ,
10691044 )
10701045 events = capture_events ()
10711046
10721047 prompt = ChatPromptTemplate .from_messages (
10731048 [
10741049 (
10751050 "system" ,
1076- system_instructions_content ,
1051+ "You are very powerful assistant, but don't know current events" ,
10771052 ),
10781053 ("user" , "{input}" ),
10791054 MessagesPlaceholder (variable_name = "agent_scratchpad" ),
@@ -1130,25 +1105,12 @@ def test_langchain_openai_tools_agent_with_config(
11301105@pytest .mark .parametrize (
11311106 "send_default_pii, include_prompts" ,
11321107 [
1133- (True , True ),
11341108 (True , False ),
11351109 (False , True ),
11361110 (False , False ),
11371111 ],
11381112)
1139- @pytest .mark .parametrize (
1140- "system_instructions_content" ,
1141- [
1142- "You are very powerful assistant, but don't know current events" ,
1143- ["You are a helpful assistant." , "Be concise and clear." ],
1144- [
1145- {"type" : "text" , "text" : "You are a helpful assistant." },
1146- {"type" : "text" , "text" : "Be concise and clear." },
1147- ],
1148- ],
1149- ids = ["string" , "list" , "blocks" ],
1150- )
1151- def test_langchain_openai_tools_agent_stream (
1113+ def test_langchain_openai_tools_agent_stream_no_prompts (
11521114 sentry_init ,
11531115 capture_events ,
11541116 send_default_pii ,
@@ -1251,68 +1213,24 @@ def test_langchain_openai_tools_agent_stream(
12511213 assert chat_spans [1 ]["data" ]["gen_ai.usage.output_tokens" ] == 28
12521214 assert chat_spans [1 ]["data" ]["gen_ai.usage.total_tokens" ] == 117
12531215
1254- if send_default_pii and include_prompts :
1255- assert "5" in chat_spans [0 ]["data" ][SPANDATA .GEN_AI_RESPONSE_TEXT ]
1256- assert "word" in tool_exec_span ["data" ][SPANDATA .GEN_AI_TOOL_INPUT ]
1257- assert 5 == int (tool_exec_span ["data" ][SPANDATA .GEN_AI_TOOL_OUTPUT ])
1258-
1259- param_id = request .node .callspec .id
1260- if "string" in param_id :
1261- assert [
1262- {
1263- "type" : "text" ,
1264- "content" : "You are very powerful assistant, but don't know current events" ,
1265- }
1266- ] == json .loads (chat_spans [0 ]["data" ][SPANDATA .GEN_AI_SYSTEM_INSTRUCTIONS ])
1267- else :
1268- assert [
1269- {
1270- "type" : "text" ,
1271- "content" : "You are a helpful assistant." ,
1272- },
1273- {
1274- "type" : "text" ,
1275- "content" : "Be concise and clear." ,
1276- },
1277- ] == json .loads (chat_spans [0 ]["data" ][SPANDATA .GEN_AI_SYSTEM_INSTRUCTIONS ])
1278-
1279- assert "5" in chat_spans [1 ]["data" ][SPANDATA .GEN_AI_RESPONSE_TEXT ]
1280-
1281- # Verify tool calls are recorded when PII is enabled
1282- assert SPANDATA .GEN_AI_RESPONSE_TOOL_CALLS in chat_spans [0 ].get ("data" , {}), (
1283- "Tool calls should be recorded when send_default_pii=True and include_prompts=True"
1284- )
1285- tool_calls_data = chat_spans [0 ]["data" ][SPANDATA .GEN_AI_RESPONSE_TOOL_CALLS ]
1286- assert isinstance (tool_calls_data , (list , str )) # Could be serialized
1287- if isinstance (tool_calls_data , str ):
1288- assert "get_word_length" in tool_calls_data
1289- elif isinstance (tool_calls_data , list ) and len (tool_calls_data ) > 0 :
1290- # Check if tool calls contain expected function name
1291- tool_call_str = str (tool_calls_data )
1292- assert "get_word_length" in tool_call_str
1293- else :
1294- assert SPANDATA .GEN_AI_SYSTEM_INSTRUCTIONS not in chat_spans [0 ].get ("data" , {})
1295- assert SPANDATA .GEN_AI_REQUEST_MESSAGES not in chat_spans [0 ].get ("data" , {})
1296- assert SPANDATA .GEN_AI_RESPONSE_TEXT not in chat_spans [0 ].get ("data" , {})
1297- assert SPANDATA .GEN_AI_SYSTEM_INSTRUCTIONS not in chat_spans [1 ].get ("data" , {})
1298- assert SPANDATA .GEN_AI_REQUEST_MESSAGES not in chat_spans [1 ].get ("data" , {})
1299- assert SPANDATA .GEN_AI_RESPONSE_TEXT not in chat_spans [1 ].get ("data" , {})
1300- assert SPANDATA .GEN_AI_TOOL_INPUT not in tool_exec_span .get ("data" , {})
1301- assert SPANDATA .GEN_AI_TOOL_OUTPUT not in tool_exec_span .get ("data" , {})
1216+ assert SPANDATA .GEN_AI_SYSTEM_INSTRUCTIONS not in chat_spans [0 ].get ("data" , {})
1217+ assert SPANDATA .GEN_AI_REQUEST_MESSAGES not in chat_spans [0 ].get ("data" , {})
1218+ assert SPANDATA .GEN_AI_RESPONSE_TEXT not in chat_spans [0 ].get ("data" , {})
1219+ assert SPANDATA .GEN_AI_SYSTEM_INSTRUCTIONS not in chat_spans [1 ].get ("data" , {})
1220+ assert SPANDATA .GEN_AI_REQUEST_MESSAGES not in chat_spans [1 ].get ("data" , {})
1221+ assert SPANDATA .GEN_AI_RESPONSE_TEXT not in chat_spans [1 ].get ("data" , {})
1222+ assert SPANDATA .GEN_AI_TOOL_INPUT not in tool_exec_span .get ("data" , {})
1223+ assert SPANDATA .GEN_AI_TOOL_OUTPUT not in tool_exec_span .get ("data" , {})
13021224
1303- # Verify tool calls are NOT recorded when PII is disabled
1304- assert SPANDATA .GEN_AI_RESPONSE_TOOL_CALLS not in chat_spans [0 ].get (
1305- "data" , {}
1306- ), (
1307- f"Tool calls should NOT be recorded when send_default_pii={ send_default_pii } "
1308- f"and include_prompts={ include_prompts } "
1309- )
1310- assert SPANDATA .GEN_AI_RESPONSE_TOOL_CALLS not in chat_spans [1 ].get (
1311- "data" , {}
1312- ), (
1313- f"Tool calls should NOT be recorded when send_default_pii={ send_default_pii } "
1314- f"and include_prompts={ include_prompts } "
1315- )
1225+ # Verify tool calls are NOT recorded when PII is disabled
1226+ assert SPANDATA .GEN_AI_RESPONSE_TOOL_CALLS not in chat_spans [0 ].get ("data" , {}), (
1227+ f"Tool calls should NOT be recorded when send_default_pii={ send_default_pii } "
1228+ f"and include_prompts={ include_prompts } "
1229+ )
1230+ assert SPANDATA .GEN_AI_RESPONSE_TOOL_CALLS not in chat_spans [1 ].get ("data" , {}), (
1231+ f"Tool calls should NOT be recorded when send_default_pii={ send_default_pii } "
1232+ f"and include_prompts={ include_prompts } "
1233+ )
13161234
13171235 # Verify finish_reasons is always an array of strings
13181236 assert chat_spans [0 ]["data" ][SPANDATA .GEN_AI_RESPONSE_FINISH_REASONS ] == [
@@ -1329,15 +1247,6 @@ def test_langchain_openai_tools_agent_stream(
13291247 assert "get_word_length" in tools_data
13301248
13311249
1332- @pytest .mark .parametrize (
1333- "send_default_pii, include_prompts" ,
1334- [
1335- (True , True ),
1336- (True , False ),
1337- (False , True ),
1338- (False , False ),
1339- ],
1340- )
13411250@pytest .mark .parametrize (
13421251 "system_instructions_content" ,
13431252 [
@@ -1350,11 +1259,9 @@ def test_langchain_openai_tools_agent_stream(
13501259 ],
13511260 ids = ["string" , "list" , "blocks" ],
13521261)
1353- def test_langchain_openai_tools_agent_stream_with_config (
1262+ def test_langchain_openai_tools_agent_stream (
13541263 sentry_init ,
13551264 capture_events ,
1356- send_default_pii ,
1357- include_prompts ,
13581265 system_instructions_content ,
13591266 request ,
13601267 get_model_response ,
@@ -1364,11 +1271,11 @@ def test_langchain_openai_tools_agent_stream_with_config(
13641271 sentry_init (
13651272 integrations = [
13661273 LangchainIntegration (
1367- include_prompts = include_prompts ,
1274+ include_prompts = True ,
13681275 )
13691276 ],
13701277 traces_sample_rate = 1.0 ,
1371- send_default_pii = send_default_pii ,
1278+ send_default_pii = True ,
13721279 )
13731280 events = capture_events ()
13741281
@@ -1399,6 +1306,160 @@ def test_langchain_openai_tools_agent_stream_with_config(
13991306 )
14001307 )
14011308
1309+ llm = ChatOpenAI (
1310+ model_name = "gpt-3.5-turbo" ,
1311+ temperature = 0 ,
1312+ openai_api_key = "badkey" ,
1313+ )
1314+ agent = create_openai_tools_agent (llm , [get_word_length ], prompt )
1315+
1316+ agent_executor = AgentExecutor (agent = agent , tools = [get_word_length ], verbose = True )
1317+
1318+ with patch .object (
1319+ llm .client ._client ._client ,
1320+ "send" ,
1321+ side_effect = [tool_response , final_response ],
1322+ ) as _ :
1323+ with start_transaction ():
1324+ list (
1325+ agent_executor .stream (
1326+ {"input" : "How many letters in the word eudca" },
1327+ {"run_name" : "my-snazzy-pipeline" },
1328+ )
1329+ )
1330+
1331+ tx = events [0 ]
1332+ assert tx ["type" ] == "transaction"
1333+ assert tx ["contexts" ]["trace" ]["origin" ] == "manual"
1334+
1335+ invoke_agent_span = next (x for x in tx ["spans" ] if x ["op" ] == "gen_ai.invoke_agent" )
1336+ chat_spans = list (x for x in tx ["spans" ] if x ["op" ] == "gen_ai.chat" )
1337+ tool_exec_span = next (x for x in tx ["spans" ] if x ["op" ] == "gen_ai.execute_tool" )
1338+
1339+ assert len (chat_spans ) == 2
1340+
1341+ assert invoke_agent_span ["origin" ] == "auto.ai.langchain"
1342+ assert chat_spans [0 ]["origin" ] == "auto.ai.langchain"
1343+ assert chat_spans [1 ]["origin" ] == "auto.ai.langchain"
1344+ assert tool_exec_span ["origin" ] == "auto.ai.langchain"
1345+
1346+ assert invoke_agent_span ["data" ]["gen_ai.function_id" ] == "my-snazzy-pipeline"
1347+
1348+ # We can't guarantee anything about the "shape" of the langchain execution graph
1349+ assert len (list (x for x in tx ["spans" ] if x ["op" ] == "gen_ai.chat" )) > 0
1350+
1351+ # Token usage is only available in newer versions of langchain (v0.2+)
1352+ # where usage_metadata is supported on AIMessageChunk
1353+ if "gen_ai.usage.input_tokens" in chat_spans [0 ]["data" ]:
1354+ assert chat_spans [0 ]["data" ]["gen_ai.usage.input_tokens" ] == 142
1355+ assert chat_spans [0 ]["data" ]["gen_ai.usage.output_tokens" ] == 50
1356+ assert chat_spans [0 ]["data" ]["gen_ai.usage.total_tokens" ] == 192
1357+
1358+ if "gen_ai.usage.input_tokens" in chat_spans [1 ]["data" ]:
1359+ assert chat_spans [1 ]["data" ]["gen_ai.usage.input_tokens" ] == 89
1360+ assert chat_spans [1 ]["data" ]["gen_ai.usage.output_tokens" ] == 28
1361+ assert chat_spans [1 ]["data" ]["gen_ai.usage.total_tokens" ] == 117
1362+
1363+ assert "5" in chat_spans [0 ]["data" ][SPANDATA .GEN_AI_RESPONSE_TEXT ]
1364+ assert "word" in tool_exec_span ["data" ][SPANDATA .GEN_AI_TOOL_INPUT ]
1365+ assert 5 == int (tool_exec_span ["data" ][SPANDATA .GEN_AI_TOOL_OUTPUT ])
1366+
1367+ param_id = request .node .callspec .id
1368+ if "string" in param_id :
1369+ assert [
1370+ {
1371+ "type" : "text" ,
1372+ "content" : "You are very powerful assistant, but don't know current events" ,
1373+ }
1374+ ] == json .loads (chat_spans [0 ]["data" ][SPANDATA .GEN_AI_SYSTEM_INSTRUCTIONS ])
1375+ else :
1376+ assert [
1377+ {
1378+ "type" : "text" ,
1379+ "content" : "You are a helpful assistant." ,
1380+ },
1381+ {
1382+ "type" : "text" ,
1383+ "content" : "Be concise and clear." ,
1384+ },
1385+ ] == json .loads (chat_spans [0 ]["data" ][SPANDATA .GEN_AI_SYSTEM_INSTRUCTIONS ])
1386+
1387+ assert "5" in chat_spans [1 ]["data" ][SPANDATA .GEN_AI_RESPONSE_TEXT ]
1388+
1389+ # Verify tool calls are recorded when PII is enabled
1390+ assert SPANDATA .GEN_AI_RESPONSE_TOOL_CALLS in chat_spans [0 ].get ("data" , {}), (
1391+ "Tool calls should be recorded when send_default_pii=True and include_prompts=True"
1392+ )
1393+ tool_calls_data = chat_spans [0 ]["data" ][SPANDATA .GEN_AI_RESPONSE_TOOL_CALLS ]
1394+ assert isinstance (tool_calls_data , (list , str )) # Could be serialized
1395+ if isinstance (tool_calls_data , str ):
1396+ assert "get_word_length" in tool_calls_data
1397+ elif isinstance (tool_calls_data , list ) and len (tool_calls_data ) > 0 :
1398+ # Check if tool calls contain expected function name
1399+ tool_call_str = str (tool_calls_data )
1400+ assert "get_word_length" in tool_call_str
1401+
1402+ # Verify finish_reasons is always an array of strings
1403+ assert chat_spans [0 ]["data" ][SPANDATA .GEN_AI_RESPONSE_FINISH_REASONS ] == [
1404+ "function_call"
1405+ ]
1406+ assert chat_spans [1 ]["data" ][SPANDATA .GEN_AI_RESPONSE_FINISH_REASONS ] == ["stop" ]
1407+
1408+ # Verify that available tools are always recorded regardless of PII settings
1409+ for chat_span in chat_spans :
1410+ tools_data = chat_span ["data" ][SPANDATA .GEN_AI_REQUEST_AVAILABLE_TOOLS ]
1411+ assert tools_data is not None , (
1412+ "Available tools should always be recorded regardless of PII settings"
1413+ )
1414+ assert "get_word_length" in tools_data
1415+
1416+
1417+ def test_langchain_openai_tools_agent_stream_with_config (
1418+ sentry_init ,
1419+ capture_events ,
1420+ system_instructions_content ,
1421+ get_model_response ,
1422+ server_side_event_chunks ,
1423+ streaming_chat_completions_model_responses ,
1424+ ):
1425+ sentry_init (
1426+ integrations = [
1427+ LangchainIntegration (
1428+ include_prompts = True ,
1429+ )
1430+ ],
1431+ traces_sample_rate = 1.0 ,
1432+ send_default_pii = True ,
1433+ )
1434+ events = capture_events ()
1435+
1436+ prompt = ChatPromptTemplate .from_messages (
1437+ [
1438+ (
1439+ "system" ,
1440+ "You are very powerful assistant, but don't know current events" ,
1441+ ),
1442+ ("user" , "{input}" ),
1443+ MessagesPlaceholder (variable_name = "agent_scratchpad" ),
1444+ ]
1445+ )
1446+
1447+ model_responses = streaming_chat_completions_model_responses ()
1448+
1449+ tool_response = get_model_response (
1450+ server_side_event_chunks (
1451+ next (model_responses ),
1452+ include_event_type = False ,
1453+ )
1454+ )
1455+
1456+ final_response = get_model_response (
1457+ server_side_event_chunks (
1458+ next (model_responses ),
1459+ include_event_type = False ,
1460+ )
1461+ )
1462+
14021463 llm = ChatOpenAI (
14031464 model_name = "gpt-3.5-turbo" ,
14041465 temperature = 0 ,
0 commit comments