Skip to content
Draft
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
20 changes: 20 additions & 0 deletions sentry_sdk/ai/_openai_completions_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from openai.types.chat import (
ChatCompletionMessageParam,
ChatCompletionSystemMessageParam,
)
from typing import Iterable, Union


def _get_system_instructions(
messages: "Iterable[Union[ChatCompletionMessageParam, str]]",
) -> "list[ChatCompletionSystemMessageParam]":
system_messages = []

for message in messages:
if isinstance(message, dict) and message.get("role") == "system":
system_messages.append(message)

return system_messages
6 changes: 6 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,12 @@ class SPANDATA:
Example: 2048
"""

GEN_AI_SYSTEM_INSTRUCTIONS = "gen_ai.system_instructions"
"""
The system instructions passed to the model.
Example: [{"type": "text", "text": "You are a helpful assistant."},{"type": "text", "text": "Be concise and clear."}]
"""

GEN_AI_REQUEST_MESSAGES = "gen_ai.request.messages"
"""
The messages passed to the model. The "content" can be a string or an array of objects.
Expand Down
42 changes: 41 additions & 1 deletion sentry_sdk/integrations/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
normalize_message_roles,
truncate_and_annotate_messages,
)
from sentry_sdk.ai._openai_completions_api import (
_get_system_instructions as _get_system_instructions_completions,
)
from sentry_sdk.consts import SPANDATA
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.scope import should_send_default_pii
Expand All @@ -35,7 +38,7 @@
)
from sentry_sdk.tracing import Span

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

try:
try:
Expand Down Expand Up @@ -193,6 +196,25 @@ def _calculate_token_usage(
)


def _get_system_instructions_responses(
input_items: "Union[ResponseInputParam, list[str]]",
) -> "list[ResponseInputItemParam]":
if isinstance(input_items, str):
return []

system_messages = []

for item in input_items:
if (
isinstance(item, dict)
and item.get("type") == "message"
and item.get("role") == "system"
):
system_messages.append(item)

return system_messages


def _get_input_messages(
kwargs: "dict[str, Any]",
) -> "Optional[Union[Iterable[Any], list[str]]]":
Expand Down Expand Up @@ -247,6 +269,15 @@ def _set_responses_api_input_data(
kwargs
)

if messages is not None:
system_instructions = _get_system_instructions_responses(messages)
set_data_normalized(
span,
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
system_instructions,
unpack=False,
)

if (
messages is not None
and len(messages) > 0
Expand Down Expand Up @@ -274,6 +305,15 @@ def _set_completions_api_input_data(
_get_input_messages(kwargs)
)

if messages is not None:
system_instructions = _get_system_instructions_completions(messages)
set_data_normalized(
span,
SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS,
system_instructions,
unpack=False,
)

if (
messages is not None
and len(messages) > 0 # type: ignore
Expand Down
Loading