Skip to content

Commit eb8c05d

Browse files
committed
add langchain test
1 parent 3286292 commit eb8c05d

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-0
lines changed

tests/integrations/langchain/test_langchain.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,3 +817,144 @@ def test_langchain_integration_with_langchain_core_only(sentry_init, capture_eve
817817
assert llm_span["data"]["gen_ai.usage.total_tokens"] == 25
818818
assert llm_span["data"]["gen_ai.usage.input_tokens"] == 10
819819
assert llm_span["data"]["gen_ai.usage.output_tokens"] == 15
820+
821+
822+
def test_langchain_message_role_mapping(sentry_init, capture_events):
823+
"""Test that message roles are properly normalized in langchain integration."""
824+
global llm_type
825+
llm_type = "openai-chat"
826+
827+
sentry_init(
828+
integrations=[LangchainIntegration(include_prompts=True)],
829+
traces_sample_rate=1.0,
830+
send_default_pii=True,
831+
)
832+
events = capture_events()
833+
834+
prompt = ChatPromptTemplate.from_messages(
835+
[
836+
("system", "You are a helpful assistant"),
837+
("human", "{input}"),
838+
MessagesPlaceholder(variable_name="agent_scratchpad"),
839+
]
840+
)
841+
842+
global stream_result_mock
843+
stream_result_mock = Mock(
844+
side_effect=[
845+
[
846+
ChatGenerationChunk(
847+
type="ChatGenerationChunk",
848+
message=AIMessageChunk(content="Test response"),
849+
),
850+
]
851+
]
852+
)
853+
854+
llm = MockOpenAI(
855+
model_name="gpt-3.5-turbo",
856+
temperature=0,
857+
openai_api_key="badkey",
858+
)
859+
agent = create_openai_tools_agent(llm, [get_word_length], prompt)
860+
agent_executor = AgentExecutor(agent=agent, tools=[get_word_length], verbose=True)
861+
862+
# Test input that should trigger message role normalization
863+
test_input = "Hello, how are you?"
864+
865+
with start_transaction():
866+
list(agent_executor.stream({"input": test_input}))
867+
868+
assert len(events) > 0
869+
tx = events[0]
870+
assert tx["type"] == "transaction"
871+
872+
# Find spans with gen_ai operation that should have message data
873+
gen_ai_spans = [
874+
span for span in tx.get("spans", []) if span.get("op", "").startswith("gen_ai")
875+
]
876+
877+
# Check if any span has message data with normalized roles
878+
message_data_found = False
879+
for span in gen_ai_spans:
880+
span_data = span.get("data", {})
881+
if SPANDATA.GEN_AI_REQUEST_MESSAGES in span_data:
882+
message_data_found = True
883+
messages_data = span_data[SPANDATA.GEN_AI_REQUEST_MESSAGES]
884+
885+
# Parse the message data (might be JSON string)
886+
if isinstance(messages_data, str):
887+
import json
888+
889+
try:
890+
messages = json.loads(messages_data)
891+
except json.JSONDecodeError:
892+
# If not valid JSON, skip this assertion
893+
continue
894+
else:
895+
messages = messages_data
896+
897+
# Verify that the input message is present and contains the test input
898+
assert isinstance(messages, list)
899+
assert len(messages) > 0
900+
901+
# The test input should be in one of the messages
902+
input_found = False
903+
for msg in messages:
904+
if isinstance(msg, dict) and test_input in str(msg.get("content", "")):
905+
input_found = True
906+
break
907+
elif isinstance(msg, str) and test_input in msg:
908+
input_found = True
909+
break
910+
911+
assert input_found, (
912+
f"Test input '{test_input}' not found in messages: {messages}"
913+
)
914+
break
915+
916+
# The message role mapping functionality is primarily tested through the normalization
917+
# that happens in the integration code. The fact that we can capture and process
918+
# the messages without errors indicates the role mapping is working correctly.
919+
assert message_data_found, "No span found with gen_ai request messages data"
920+
921+
922+
def test_langchain_message_role_normalization_units():
923+
"""Test the message role normalization functions directly."""
924+
from sentry_sdk.ai.utils import normalize_message_role, normalize_message_roles
925+
926+
# Test individual role normalization
927+
assert normalize_message_role("ai") == "assistant"
928+
assert normalize_message_role("human") == "user"
929+
assert normalize_message_role("tool_call") == "tool"
930+
assert normalize_message_role("system") == "system"
931+
assert normalize_message_role("user") == "user"
932+
assert normalize_message_role("assistant") == "assistant"
933+
assert normalize_message_role("tool") == "tool"
934+
935+
# Test unknown role (should remain unchanged)
936+
assert normalize_message_role("unknown_role") == "unknown_role"
937+
938+
# Test message list normalization
939+
test_messages = [
940+
{"role": "human", "content": "Hello"},
941+
{"role": "ai", "content": "Hi there!"},
942+
{"role": "tool_call", "content": "function_call"},
943+
{"role": "system", "content": "You are helpful"},
944+
{"content": "Message without role"},
945+
"string message",
946+
]
947+
948+
normalized = normalize_message_roles(test_messages)
949+
950+
# Verify the original messages are not modified
951+
assert test_messages[0]["role"] == "human" # Original unchanged
952+
assert test_messages[1]["role"] == "ai" # Original unchanged
953+
954+
# Verify the normalized messages have correct roles
955+
assert normalized[0]["role"] == "user" # human -> user
956+
assert normalized[1]["role"] == "assistant" # ai -> assistant
957+
assert normalized[2]["role"] == "tool" # tool_call -> tool
958+
assert normalized[3]["role"] == "system" # system unchanged
959+
assert "role" not in normalized[4] # Message without role unchanged
960+
assert normalized[5] == "string message" # String message unchanged

0 commit comments

Comments
 (0)