Skip to content

Commit 934bbd7

Browse files
feat(openai): Gate Responses API inputs behind data collection (#6964)
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 --------- Co-authored-by: Alexander Alderman Webb <alexander.webb@sentry.io>
1 parent a855efe commit 934bbd7

2 files changed

Lines changed: 301 additions & 40 deletions

File tree

sentry_sdk/integrations/openai.py

Lines changed: 57 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
@@ -48,6 +48,7 @@
4848
from sentry_sdk.utils import (
4949
capture_internal_exceptions,
5050
event_from_exception,
51+
has_data_collection_enabled,
5152
reraise,
5253
)
5354

@@ -329,19 +330,11 @@ def _set_responses_api_input_data(
329330
kwargs: "dict[str, Any]",
330331
integration: "OpenAIIntegration",
331332
) -> None:
332-
explicit_instructions: "Union[Optional[str], Omit]" = kwargs.get("instructions")
333-
messages: "Optional[Union[str, ResponseInputParam]]" = kwargs.get("input")
334-
335333
set_on_span = (
336334
span.set_attribute if isinstance(span, StreamedSpan) else span.set_data
337335
)
338336

339-
tools = kwargs.get("tools")
340-
if tools is not None and _is_given(tools):
341-
set_on_span(
342-
SPANDATA.GEN_AI_TOOL_DEFINITIONS,
343-
json.dumps(_transform_tool_definitions_responses(tools)),
344-
)
337+
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
345338

346339
model = kwargs.get("model")
347340
if model is not None:
@@ -376,54 +369,81 @@ def _set_responses_api_input_data(
376369
reasoning["effort"],
377370
)
378371

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

400-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
396+
if has_data_collection_enabled(client_options):
397+
# This takes precedence over the global data collection settings
398+
if not integration.include_prompts:
399+
return
400+
if not client_options["data_collection"]["gen_ai"]["inputs"]:
401+
return
402+
elif not should_send_default_pii() or not integration.include_prompts:
401403
return
402404

405+
explicit_instructions: "Union[Optional[str], Omit]" = kwargs.get("instructions")
406+
has_explicit_instructions = explicit_instructions is not None and _is_given(
407+
explicit_instructions
408+
)
409+
messages: "Optional[Union[str, ResponseInputParam]]" = kwargs.get("input")
410+
instructions_text_parts: "list[TextPart]" = []
411+
403412
if messages is None:
404-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
413+
if has_explicit_instructions:
414+
set_on_span(
415+
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
416+
json.dumps(
417+
[
418+
{
419+
"type": "text",
420+
"content": explicit_instructions,
421+
}
422+
]
423+
),
424+
)
425+
# No messages to record (only instructions at most)
405426
return
406427

407-
instructions_text_parts: "list[TextPart]" = []
408-
if explicit_instructions is not None and _is_given(explicit_instructions):
428+
if has_explicit_instructions:
409429
instructions_text_parts.append(
410430
{
411431
"type": "text",
412-
"content": explicit_instructions,
432+
"content": cast(str, explicit_instructions),
413433
}
414434
)
415435

416436
system_instructions = _get_system_instructions_responses(messages)
417437
# Deliberate use of function accepting completions API type because
418438
# of shared structure FOR THIS PURPOSE ONLY.
419439
instructions_text_parts += _transform_system_instructions(system_instructions)
420-
421440
if len(instructions_text_parts) > 0:
422441
set_on_span(
423442
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
424443
json.dumps(instructions_text_parts),
425444
)
426445

446+
# Input was provided as a single string
427447
if isinstance(messages, str):
428448
normalized_messages = normalize_message_roles([messages]) # type: ignore
429449
client = sentry_sdk.get_client()
@@ -437,10 +457,9 @@ def _set_responses_api_input_data(
437457
set_data_normalized(
438458
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
439459
)
440-
441-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
442460
return
443461

462+
# Input was provided as a list (potentially a multi-turn conversation)
444463
non_system_messages = [
445464
message for message in messages if not _is_system_instruction_responses(message)
446465
]
@@ -458,8 +477,6 @@ def _set_responses_api_input_data(
458477
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
459478
)
460479

461-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
462-
463480

464481
def _set_completions_api_input_data(
465482
span: "Union[Span, StreamedSpan]",

0 commit comments

Comments
 (0)