Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4f25a56
feat(openai): Set system instruction attribute
alexander-alderman-webb Jan 21, 2026
921a82f
Merge branch 'webb/openai-separate-input-handling' into webb/openai-a…
alexander-alderman-webb Jan 21, 2026
9f5d801
.
alexander-alderman-webb Jan 21, 2026
d66ffe1
use specific openai types
alexander-alderman-webb Jan 21, 2026
ac3ce00
wip
alexander-alderman-webb Jan 22, 2026
ef9fe6f
.
alexander-alderman-webb Jan 22, 2026
ce84a29
.
alexander-alderman-webb Jan 22, 2026
dee9930
remove test
alexander-alderman-webb Jan 22, 2026
cb00ab3
.
alexander-alderman-webb Jan 22, 2026
26b932b
.
alexander-alderman-webb Jan 22, 2026
04dc92c
.
alexander-alderman-webb Jan 22, 2026
c7263ea
edge case
alexander-alderman-webb Jan 22, 2026
d947899
full responses api tests
alexander-alderman-webb Jan 22, 2026
8cbeac1
remove sentry_sdk/ai/_openai_completions_api.py
alexander-alderman-webb Jan 22, 2026
bcebcc8
fix test
alexander-alderman-webb Jan 22, 2026
87dd8fe
Merge branch 'webb/openai-separate-input-handling' into webb/openai-a…
alexander-alderman-webb Jan 23, 2026
3f5ad11
more defensive checks in case input is not iterable
alexander-alderman-webb Jan 23, 2026
a8840d5
more early returns
alexander-alderman-webb Jan 26, 2026
382e933
revert unrelated tests
alexander-alderman-webb Jan 26, 2026
179d59e
revert unrelated change
alexander-alderman-webb Jan 26, 2026
12cb219
address comment
alexander-alderman-webb Jan 26, 2026
a6152fe
remove unused type ignore
alexander-alderman-webb Jan 26, 2026
5085fd8
split off responses API changes
alexander-alderman-webb Jan 26, 2026
7fc64f0
remove unused functions
alexander-alderman-webb Jan 26, 2026
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
100 changes: 88 additions & 12 deletions sentry_sdk/integrations/openai.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sys
from functools import wraps
from collections.abc import Iterable

import sentry_sdk
from sentry_sdk import consts
Expand All @@ -25,7 +26,6 @@
if TYPE_CHECKING:
from typing import (
Any,
Iterable,
List,
Optional,
Callable,
Expand All @@ -34,8 +34,10 @@
Union,
)
from sentry_sdk.tracing import Span
from sentry_sdk._types import TextPart

from openai.types.responses import ResponseInputParam
from openai.types.responses import ResponseInputParam, ResponseInputItemParam
from openai import Omit

try:
try:
Expand All @@ -52,7 +54,11 @@
from openai.resources import Embeddings, AsyncEmbeddings

if TYPE_CHECKING:
from openai.types.chat import ChatCompletionMessageParam, ChatCompletionChunk
from openai.types.chat import (
ChatCompletionMessageParam,
ChatCompletionChunk,
ChatCompletionSystemMessageParam,
)
except ImportError:
raise DidNotEnable("OpenAI not installed")

Expand Down Expand Up @@ -193,6 +199,45 @@ def _calculate_token_usage(
)


def _is_system_instruction_completions(message: "ChatCompletionMessageParam") -> bool:
return isinstance(message, dict) and message.get("role") == "system"


def _get_system_instructions_completions(
messages: "Iterable[ChatCompletionMessageParam]",
) -> "list[ChatCompletionMessageParam]":
if not isinstance(messages, Iterable):
return []

return [
message for message in messages if _is_system_instruction_completions(message)
]


def _transform_system_instructions(
system_instructions: "list[ChatCompletionSystemMessageParam]",
) -> "list[TextPart]":
instruction_text_parts: "list[TextPart]" = []

for instruction in system_instructions:
if not isinstance(instruction, dict):
continue

content = instruction.get("content")

if isinstance(content, str):
instruction_text_parts.append({"type": "text", "content": content})

elif isinstance(content, list):
for part in content:
if isinstance(part, dict) and part.get("type") == "text":
text = part.get("text", "")
if text:
instruction_text_parts.append({"type": "text", "content": text})

return instruction_text_parts


def _get_input_messages(
kwargs: "dict[str, Any]",
) -> "Optional[Union[Iterable[Any], list[str]]]":
Expand Down Expand Up @@ -270,17 +315,48 @@ def _set_completions_api_input_data(
kwargs: "dict[str, Any]",
integration: "OpenAIIntegration",
) -> None:
messages: "Optional[Union[Iterable[ChatCompletionMessageParam], list[str]]]" = (
_get_input_messages(kwargs)
messages: "Optional[Union[str, Iterable[ChatCompletionMessageParam]]]" = kwargs.get(
"messages"
)

if (
messages is not None
and len(messages) > 0 # type: ignore
and should_send_default_pii()
and integration.include_prompts
):
normalized_messages = normalize_message_roles(messages) # type: ignore
if not should_send_default_pii() or not integration.include_prompts:
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "responses")
_commmon_set_input_data(span, kwargs)
return

if messages is None:
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "chat")
_commmon_set_input_data(span, kwargs)
return

system_instructions = _get_system_instructions_completions(messages)
if len(system_instructions) > 0:
set_data_normalized(
span,
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
_transform_system_instructions(system_instructions),
unpack=False,
)

if isinstance(messages, str):
normalized_messages = normalize_message_roles([messages]) # type: ignore
scope = sentry_sdk.get_current_scope()
messages_data = truncate_and_annotate_messages(normalized_messages, span, scope)
if messages_data is not None:
set_data_normalized(
span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages_data, unpack=False
)
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "chat")
_commmon_set_input_data(span, kwargs)
return

non_system_messages = [
message
for message in messages
if not _is_system_instruction_completions(message)
]
if len(non_system_messages) > 0:
normalized_messages = normalize_message_roles(non_system_messages) # type: ignore
scope = sentry_sdk.get_current_scope()
messages_data = truncate_and_annotate_messages(normalized_messages, span, scope)
if messages_data is not None:
Expand Down
Loading
Loading