|
19 | 19 | has_span_streaming_enabled, |
20 | 20 | should_truncate_gen_ai_input, |
21 | 21 | ) |
22 | | -from sentry_sdk.utils import package_version, safe_serialize |
| 22 | +from sentry_sdk.utils import ( |
| 23 | + has_data_collection_enabled, |
| 24 | + package_version, |
| 25 | + safe_serialize, |
| 26 | +) |
23 | 27 |
|
24 | 28 | try: |
25 | 29 | from langgraph.errors import GraphBubbleUp |
@@ -56,6 +60,24 @@ def setup_once() -> None: |
56 | 60 | Pregel.ainvoke = _wrap_pregel_ainvoke(Pregel.ainvoke) |
57 | 61 |
|
58 | 62 |
|
| 63 | +def _should_record_inputs(integration: "LanggraphIntegration") -> bool: |
| 64 | + client = sentry_sdk.get_client() |
| 65 | + if has_data_collection_enabled(client.options): |
| 66 | + return bool(client.options["data_collection"]["gen_ai"]["inputs"]) |
| 67 | + |
| 68 | + # To remove once data collection has been fully rolled out |
| 69 | + return should_send_default_pii() and integration.include_prompts |
| 70 | + |
| 71 | + |
| 72 | +def _should_record_outputs(integration: "LanggraphIntegration") -> bool: |
| 73 | + client = sentry_sdk.get_client() |
| 74 | + if has_data_collection_enabled(client.options): |
| 75 | + return bool(client.options["data_collection"]["gen_ai"]["outputs"]) |
| 76 | + |
| 77 | + # To remove once data collection has been fully rolled out |
| 78 | + return should_send_default_pii() and integration.include_prompts |
| 79 | + |
| 80 | + |
59 | 81 | def _get_graph_name(graph_obj: "Any") -> "Optional[str]": |
60 | 82 | for attr in ["name", "graph_name", "__name__", "_name"]: |
61 | 83 | if hasattr(graph_obj, attr): |
@@ -156,7 +178,13 @@ def new_compile(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": |
156 | 178 | tools = list(data.tools_by_name.keys()) |
157 | 179 |
|
158 | 180 | if tools is not None: |
159 | | - span.set_data(SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, tools) |
| 181 | + # Available tools aren't gated on the legacy PII settings, so they're |
| 182 | + # only gated when data collection has been configured. |
| 183 | + if has_data_collection_enabled(client.options): |
| 184 | + if client.options["data_collection"]["gen_ai"]["inputs"]: |
| 185 | + span.set_data(SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, tools) |
| 186 | + else: |
| 187 | + span.set_data(SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, tools) |
160 | 188 |
|
161 | 189 | return compiled_graph |
162 | 190 |
|
@@ -191,18 +219,13 @@ def new_invoke(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": |
191 | 219 |
|
192 | 220 | # Store input messages to later compare with output |
193 | 221 | input_messages = None |
194 | | - if ( |
195 | | - len(args) > 0 |
196 | | - and should_send_default_pii() |
197 | | - and integration.include_prompts |
198 | | - ): |
| 222 | + if len(args) > 0: |
199 | 223 | input_messages = _parse_langgraph_messages(args[0]) |
200 | | - if input_messages: |
| 224 | + if input_messages and _should_record_inputs(integration): |
201 | 225 | normalized_input_messages = normalize_message_roles( |
202 | 226 | input_messages |
203 | 227 | ) |
204 | 228 |
|
205 | | - client = sentry_sdk.get_client() |
206 | 229 | scope = sentry_sdk.get_current_scope() |
207 | 230 | messages_data = ( |
208 | 231 | truncate_and_annotate_messages( |
@@ -238,18 +261,13 @@ def new_invoke(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": |
238 | 261 |
|
239 | 262 | # Store input messages to later compare with output |
240 | 263 | input_messages = None |
241 | | - if ( |
242 | | - len(args) > 0 |
243 | | - and should_send_default_pii() |
244 | | - and integration.include_prompts |
245 | | - ): |
| 264 | + if len(args) > 0: |
246 | 265 | input_messages = _parse_langgraph_messages(args[0]) |
247 | | - if input_messages: |
| 266 | + if input_messages and _should_record_inputs(integration): |
248 | 267 | normalized_input_messages = normalize_message_roles( |
249 | 268 | input_messages |
250 | 269 | ) |
251 | 270 |
|
252 | | - client = sentry_sdk.get_client() |
253 | 271 | scope = sentry_sdk.get_current_scope() |
254 | 272 | messages_data = ( |
255 | 273 | truncate_and_annotate_messages( |
@@ -302,18 +320,13 @@ async def new_ainvoke(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": |
302 | 320 | span.set_attribute(SPANDATA.GEN_AI_AGENT_NAME, graph_name) |
303 | 321 |
|
304 | 322 | input_messages = None |
305 | | - if ( |
306 | | - len(args) > 0 |
307 | | - and should_send_default_pii() |
308 | | - and integration.include_prompts |
309 | | - ): |
| 323 | + if len(args) > 0: |
310 | 324 | input_messages = _parse_langgraph_messages(args[0]) |
311 | | - if input_messages: |
| 325 | + if input_messages and _should_record_inputs(integration): |
312 | 326 | normalized_input_messages = normalize_message_roles( |
313 | 327 | input_messages |
314 | 328 | ) |
315 | 329 |
|
316 | | - client = sentry_sdk.get_client() |
317 | 330 | scope = sentry_sdk.get_current_scope() |
318 | 331 | messages_data = ( |
319 | 332 | truncate_and_annotate_messages( |
@@ -348,16 +361,11 @@ async def new_ainvoke(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": |
348 | 361 | span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, "invoke_agent") |
349 | 362 |
|
350 | 363 | input_messages = None |
351 | | - if ( |
352 | | - len(args) > 0 |
353 | | - and should_send_default_pii() |
354 | | - and integration.include_prompts |
355 | | - ): |
| 364 | + if len(args) > 0: |
356 | 365 | input_messages = _parse_langgraph_messages(args[0]) |
357 | | - if input_messages: |
| 366 | + if input_messages and _should_record_inputs(integration): |
358 | 367 | normalized_input_messages = normalize_message_roles(input_messages) |
359 | 368 |
|
360 | | - client = sentry_sdk.get_client() |
361 | 369 | scope = sentry_sdk.get_current_scope() |
362 | 370 | messages_data = ( |
363 | 371 | truncate_and_annotate_messages( |
@@ -497,22 +505,22 @@ def _set_response_attributes( |
497 | 505 | _set_usage_data(span, new_messages) |
498 | 506 | _set_response_model_name(span, new_messages) |
499 | 507 |
|
500 | | - if not (should_send_default_pii() and integration.include_prompts): |
501 | | - return |
502 | | - |
503 | | - llm_response_text = _extract_llm_response_text(new_messages) |
504 | | - if llm_response_text: |
505 | | - set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, llm_response_text) |
506 | | - elif new_messages: |
507 | | - set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, new_messages) |
508 | | - else: |
509 | | - set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, result) |
510 | | - |
511 | | - tool_calls = _extract_tool_calls(new_messages) |
512 | | - if tool_calls: |
513 | | - set_data_normalized( |
514 | | - span, |
515 | | - SPANDATA.GEN_AI_RESPONSE_TOOL_CALLS, |
516 | | - safe_serialize(tool_calls), |
517 | | - unpack=False, |
518 | | - ) |
| 508 | + if _should_record_outputs(integration): |
| 509 | + llm_response_text = _extract_llm_response_text(new_messages) |
| 510 | + if llm_response_text: |
| 511 | + set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, llm_response_text) |
| 512 | + elif new_messages: |
| 513 | + set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, new_messages) |
| 514 | + else: |
| 515 | + set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, result) |
| 516 | + |
| 517 | + # Tool calls are an input to the model, so they're gated on inputs |
| 518 | + if _should_record_inputs(integration): |
| 519 | + tool_calls = _extract_tool_calls(new_messages) |
| 520 | + if tool_calls: |
| 521 | + set_data_normalized( |
| 522 | + span, |
| 523 | + SPANDATA.GEN_AI_RESPONSE_TOOL_CALLS, |
| 524 | + safe_serialize(tool_calls), |
| 525 | + unpack=False, |
| 526 | + ) |
0 commit comments