Skip to content

Commit 16dacf9

Browse files
committed
fix(ai): add message truncation in langchain
1 parent 814cd5a commit 16dacf9

File tree

2 files changed

+108
-21
lines changed

2 files changed

+108
-21
lines changed

sentry_sdk/integrations/langchain.py

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
normalize_message_roles,
1010
set_data_normalized,
1111
get_start_span_function,
12+
truncate_and_annotate_messages,
1213
)
1314
from sentry_sdk.consts import OP, SPANDATA
1415
from sentry_sdk.integrations import DidNotEnable, Integration
@@ -221,12 +222,17 @@ def on_llm_start(
221222
}
222223
for prompt in prompts
223224
]
224-
set_data_normalized(
225-
span,
226-
SPANDATA.GEN_AI_REQUEST_MESSAGES,
227-
normalized_messages,
228-
unpack=False,
225+
scope = sentry_sdk.get_current_scope()
226+
messages_data = truncate_and_annotate_messages(
227+
normalized_messages, span, scope
229228
)
229+
if messages_data is not None:
230+
set_data_normalized(
231+
span,
232+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
233+
messages_data,
234+
unpack=False,
235+
)
230236

231237
def on_chat_model_start(self, serialized, messages, *, run_id, **kwargs):
232238
# type: (SentryLangchainCallback, Dict[str, Any], List[List[BaseMessage]], UUID, Any) -> Any
@@ -278,13 +284,17 @@ def on_chat_model_start(self, serialized, messages, *, run_id, **kwargs):
278284
self._normalize_langchain_message(message)
279285
)
280286
normalized_messages = normalize_message_roles(normalized_messages)
281-
282-
set_data_normalized(
283-
span,
284-
SPANDATA.GEN_AI_REQUEST_MESSAGES,
285-
normalized_messages,
286-
unpack=False,
287+
scope = sentry_sdk.get_current_scope()
288+
messages_data = truncate_and_annotate_messages(
289+
normalized_messages, span, scope
287290
)
291+
if messages_data is not None:
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
@@ -758,12 +768,17 @@ def new_invoke(self, *args, **kwargs):
758768
and integration.include_prompts
759769
):
760770
normalized_messages = normalize_message_roles([input])
761-
set_data_normalized(
762-
span,
763-
SPANDATA.GEN_AI_REQUEST_MESSAGES,
764-
normalized_messages,
765-
unpack=False,
771+
scope = sentry_sdk.get_current_scope()
772+
messages_data = truncate_and_annotate_messages(
773+
normalized_messages, span, scope
766774
)
775+
if messages_data is not None:
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 (
@@ -813,12 +828,17 @@ def new_stream(self, *args, **kwargs):
813828
and integration.include_prompts
814829
):
815830
normalized_messages = normalize_message_roles([input])
816-
set_data_normalized(
817-
span,
818-
SPANDATA.GEN_AI_REQUEST_MESSAGES,
819-
normalized_messages,
820-
unpack=False,
831+
scope = sentry_sdk.get_current_scope()
832+
messages_data = truncate_and_annotate_messages(
833+
normalized_messages, span, scope
821834
)
835+
if messages_data is not None:
836+
set_data_normalized(
837+
span,
838+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
839+
messages_data,
840+
unpack=False,
841+
)
822842

823843
# Run the agent
824844
result = f(self, *args, **kwargs)

tests/integrations/langchain/test_langchain.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,3 +958,70 @@ def test_langchain_message_role_normalization_units():
958958
assert normalized[3]["role"] == "system" # system unchanged
959959
assert "role" not in normalized[4] # Message without role unchanged
960960
assert normalized[5] == "string message" # String message unchanged
961+
962+
963+
def test_langchain_message_truncation(sentry_init, capture_events):
964+
"""Test that large messages are truncated properly in Langchain integration."""
965+
from langchain_core.outputs import LLMResult, Generation
966+
967+
sentry_init(
968+
integrations=[LangchainIntegration(include_prompts=True)],
969+
traces_sample_rate=1.0,
970+
send_default_pii=True,
971+
)
972+
events = capture_events()
973+
974+
callback = SentryLangchainCallback(max_span_map_size=100, include_prompts=True)
975+
976+
run_id = "12345678-1234-1234-1234-123456789012"
977+
serialized = {"_type": "openai-chat", "model_name": "gpt-3.5-turbo"}
978+
979+
large_content = (
980+
"This is a very long message that will exceed our size limits. " * 1000
981+
)
982+
prompts = [large_content, large_content, large_content, large_content]
983+
984+
with start_transaction():
985+
callback.on_llm_start(
986+
serialized=serialized,
987+
prompts=prompts,
988+
run_id=run_id,
989+
invocation_params={
990+
"temperature": 0.7,
991+
"max_tokens": 100,
992+
"model": "gpt-3.5-turbo",
993+
},
994+
)
995+
996+
response = LLMResult(
997+
generations=[[Generation(text="The response")]],
998+
llm_output={
999+
"token_usage": {
1000+
"total_tokens": 25,
1001+
"prompt_tokens": 10,
1002+
"completion_tokens": 15,
1003+
}
1004+
},
1005+
)
1006+
callback.on_llm_end(response=response, run_id=run_id)
1007+
1008+
assert len(events) > 0
1009+
tx = events[0]
1010+
assert tx["type"] == "transaction"
1011+
1012+
llm_spans = [
1013+
span for span in tx.get("spans", []) if span.get("op") == "gen_ai.pipeline"
1014+
]
1015+
assert len(llm_spans) > 0
1016+
1017+
llm_span = llm_spans[0]
1018+
assert SPANDATA.GEN_AI_REQUEST_MESSAGES in llm_span["data"]
1019+
1020+
messages_data = llm_span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]
1021+
assert isinstance(messages_data, str)
1022+
1023+
import json
1024+
1025+
parsed_messages = json.loads(messages_data)
1026+
assert isinstance(parsed_messages, list)
1027+
assert len(parsed_messages) <= len(prompts)

0 commit comments

Comments
 (0)