Skip to content

Commit 7edb96d

Browse files
committed
feat(openai): Gate Responses API inputs behind data collection
Respect `data_collection.gen_ai.inputs` when recording Responses API messages, system instructions, and tool definitions, while preserving legacy `send_default_pii` behavior. Add coverage for enabled, disabled, default, and instruction-only inputs. Refs PY-2588
1 parent 1d83903 commit 7edb96d

2 files changed

Lines changed: 281 additions & 40 deletions

File tree

sentry_sdk/integrations/openai.py

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import time
44
from collections.abc import Iterable
55
from functools import wraps
6-
from typing import TYPE_CHECKING
6+
from typing import TYPE_CHECKING, cast
77

88
import sentry_sdk
99
from sentry_sdk import consts
@@ -42,6 +42,7 @@
4242
from sentry_sdk.utils import (
4343
capture_internal_exceptions,
4444
event_from_exception,
45+
has_data_collection_enabled,
4546
reraise,
4647
safe_serialize,
4748
)
@@ -324,18 +325,12 @@ def _set_responses_api_input_data(
324325
kwargs: "dict[str, Any]",
325326
integration: "OpenAIIntegration",
326327
) -> None:
327-
explicit_instructions: "Union[Optional[str], Omit]" = kwargs.get("instructions")
328-
messages: "Optional[Union[str, ResponseInputParam]]" = kwargs.get("input")
329-
330-
tools = kwargs.get("tools")
331-
if tools is not None and _is_given(tools) and len(tools) > 0:
332-
set_data_normalized(
333-
span, SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, safe_serialize(tools)
334-
)
335-
336328
set_on_span = (
337329
span.set_attribute if isinstance(span, StreamedSpan) else span.set_data
338330
)
331+
332+
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
333+
339334
model = kwargs.get("model")
340335
if model is not None:
341336
set_on_span(SPANDATA.GEN_AI_REQUEST_MODEL, model)
@@ -369,54 +364,73 @@ def _set_responses_api_input_data(
369364
reasoning["effort"],
370365
)
371366

372-
if not should_send_default_pii() or not integration.include_prompts:
373-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
374-
return
375-
376-
if (
377-
messages is None
378-
and explicit_instructions is not None
379-
and _is_given(explicit_instructions)
380-
):
381-
set_on_span(
382-
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
383-
json.dumps(
384-
[
385-
{
386-
"type": "text",
387-
"content": explicit_instructions,
388-
}
389-
]
390-
),
391-
)
367+
client_options = sentry_sdk.get_client().options
368+
if has_data_collection_enabled(client_options):
369+
if client_options["data_collection"]["gen_ai"]["inputs"]:
370+
tools = kwargs.get("tools")
371+
if tools is not None and _is_given(tools) and len(tools) > 0:
372+
set_data_normalized(
373+
span, SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, safe_serialize(tools)
374+
)
375+
else:
376+
# Pre-data collection this was always set, so this needs to be left here for now until
377+
# we deprecate `send_default_pii`. Once we do, this 'else' branch should be removed,
378+
# and the above branch placed below the "if not should_send_default_pii() or not integration.include_prompts"
379+
# line below
380+
tools = kwargs.get("tools")
381+
if tools is not None and _is_given(tools) and len(tools) > 0:
382+
set_data_normalized(
383+
span, SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, safe_serialize(tools)
384+
)
392385

393-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
386+
if has_data_collection_enabled(client_options):
387+
if not client_options["data_collection"]["gen_ai"]["inputs"]:
388+
return
389+
elif not should_send_default_pii() or not integration.include_prompts:
394390
return
395391

392+
explicit_instructions: "Union[Optional[str], Omit]" = kwargs.get("instructions")
393+
has_explicit_instructions = explicit_instructions is not None and _is_given(
394+
explicit_instructions
395+
)
396+
messages: "Optional[Union[str, ResponseInputParam]]" = kwargs.get("input")
397+
instructions_text_parts: "list[TextPart]" = []
398+
396399
if messages is None:
397-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
400+
if has_explicit_instructions:
401+
set_on_span(
402+
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
403+
json.dumps(
404+
[
405+
{
406+
"type": "text",
407+
"content": explicit_instructions,
408+
}
409+
]
410+
),
411+
)
412+
# No messages to record (only instructions at most)
398413
return
399414

400-
instructions_text_parts: "list[TextPart]" = []
401-
if explicit_instructions is not None and _is_given(explicit_instructions):
415+
if has_explicit_instructions:
402416
instructions_text_parts.append(
403417
{
404418
"type": "text",
405-
"content": explicit_instructions,
419+
"content": cast(str, explicit_instructions),
406420
}
407421
)
408422

409423
system_instructions = _get_system_instructions_responses(messages)
410424
# Deliberate use of function accepting completions API type because
411425
# of shared structure FOR THIS PURPOSE ONLY.
412426
instructions_text_parts += _transform_system_instructions(system_instructions)
413-
414427
if len(instructions_text_parts) > 0:
415428
set_on_span(
416429
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
417430
json.dumps(instructions_text_parts),
418431
)
419432

433+
# Input was provided as a single string
420434
if isinstance(messages, str):
421435
normalized_messages = normalize_message_roles([messages]) # type: ignore
422436
client = sentry_sdk.get_client()
@@ -430,10 +444,9 @@ def _set_responses_api_input_data(
430444
set_data_normalized(
431445
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
432446
)
433-
434-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
435447
return
436448

449+
# Input was provided as a list (potentially a multi-turn conversation)
437450
non_system_messages = [
438451
message for message in messages if not _is_system_instruction_responses(message)
439452
]
@@ -451,8 +464,6 @@ def _set_responses_api_input_data(
451464
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
452465
)
453466

454-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
455-
456467

457468
def _set_completions_api_input_data(
458469
span: "Union[Span, StreamedSpan]",

0 commit comments

Comments
 (0)