Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 57 additions & 40 deletions sentry_sdk/integrations/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import time
from collections.abc import Iterable
from functools import wraps
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, cast

import sentry_sdk
from sentry_sdk import consts
Expand Down Expand Up @@ -42,6 +42,7 @@
from sentry_sdk.utils import (
capture_internal_exceptions,
event_from_exception,
has_data_collection_enabled,
reraise,
safe_serialize,
)
Expand Down Expand Up @@ -324,18 +325,12 @@ def _set_responses_api_input_data(
kwargs: "dict[str, Any]",
integration: "OpenAIIntegration",
) -> None:
explicit_instructions: "Union[Optional[str], Omit]" = kwargs.get("instructions")
messages: "Optional[Union[str, ResponseInputParam]]" = kwargs.get("input")

tools = kwargs.get("tools")
if tools is not None and _is_given(tools) and len(tools) > 0:
set_data_normalized(
span, SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, safe_serialize(tools)
)

set_on_span = (
span.set_attribute if isinstance(span, StreamedSpan) else span.set_data
)

set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")

model = kwargs.get("model")
if model is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_MODEL, model)
Expand Down Expand Up @@ -369,54 +364,79 @@ def _set_responses_api_input_data(
reasoning["effort"],
)

if not should_send_default_pii() or not integration.include_prompts:
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
return

if (
messages is None
and explicit_instructions is not None
and _is_given(explicit_instructions)
):
set_on_span(
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
json.dumps(
[
{
"type": "text",
"content": explicit_instructions,
}
]
),
)
client_options = sentry_sdk.get_client().options
if has_data_collection_enabled(client_options):
if (
integration.include_prompts
and client_options["data_collection"]["gen_ai"]["inputs"]
):
tools = kwargs.get("tools")
if tools is not None and _is_given(tools) and len(tools) > 0:
set_data_normalized(
span, SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, safe_serialize(tools)
)
else:
# Pre-data collection this was always set, so this needs to be left here for now until
# we deprecate `send_default_pii`. Once we do, this 'else' branch should be removed,
# and the above branch placed below the "if not should_send_default_pii() or not integration.include_prompts"
# line below
tools = kwargs.get("tools")
if tools is not None and _is_given(tools) and len(tools) > 0:
set_data_normalized(
span, SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, safe_serialize(tools)
)

set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
if has_data_collection_enabled(client_options):
# This takes precedence over the global data collection settings
if not integration.include_prompts:
return
if not client_options["data_collection"]["gen_ai"]["inputs"]:
return
elif not should_send_default_pii() or not integration.include_prompts:
return

explicit_instructions: "Union[Optional[str], Omit]" = kwargs.get("instructions")
has_explicit_instructions = explicit_instructions is not None and _is_given(
explicit_instructions
)
messages: "Optional[Union[str, ResponseInputParam]]" = kwargs.get("input")
instructions_text_parts: "list[TextPart]" = []

if messages is None:
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
if has_explicit_instructions:
set_on_span(
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
json.dumps(
[
{
"type": "text",
"content": explicit_instructions,
}
]
),
)
# No messages to record (only instructions at most)
return

instructions_text_parts: "list[TextPart]" = []
if explicit_instructions is not None and _is_given(explicit_instructions):
if has_explicit_instructions:
instructions_text_parts.append(
{
"type": "text",
"content": explicit_instructions,
"content": cast(str, explicit_instructions),
}
)

system_instructions = _get_system_instructions_responses(messages)
# Deliberate use of function accepting completions API type because
# of shared structure FOR THIS PURPOSE ONLY.
instructions_text_parts += _transform_system_instructions(system_instructions)

if len(instructions_text_parts) > 0:
set_on_span(
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
json.dumps(instructions_text_parts),
)

# Input was provided as a single string
if isinstance(messages, str):
normalized_messages = normalize_message_roles([messages]) # type: ignore
client = sentry_sdk.get_client()
Expand All @@ -430,10 +450,9 @@ def _set_responses_api_input_data(
set_data_normalized(
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
)

set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
return

# Input was provided as a list (potentially a multi-turn conversation)
non_system_messages = [
message for message in messages if not _is_system_instruction_responses(message)
]
Expand All @@ -451,8 +470,6 @@ def _set_responses_api_input_data(
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
)

set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")


def _set_completions_api_input_data(
span: "Union[Span, StreamedSpan]",
Expand Down
Loading
Loading