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
45 changes: 45 additions & 0 deletions sentry_sdk/integrations/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,21 @@ def _set_common_output_data(

# Chat Completions API
if hasattr(response, "choices") and response.choices is not None:
finish_reasons = [
choice.finish_reason
for choice in response.choices
if hasattr(choice, "finish_reason") and choice.finish_reason is not None
]
if len(finish_reasons) > 0:
if isinstance(span, StreamedSpan):
span.set_attribute(
SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS, finish_reasons
)
else:
span.set_data(
SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS, finish_reasons
)

if has_data_collection_enabled(client.options):
if client.options["data_collection"]["gen_ai"]["outputs"]:
response_text = [
Expand Down Expand Up @@ -1046,6 +1061,7 @@ def _wrap_synchronous_completions_chunk_iterator(
streaming_message_total_token_usage = None
client = sentry_sdk.get_client()

finish_reasons: "list[str]" = []
for x in old_iterator:
if isinstance(span, StreamedSpan):
span.set_attribute(SPANDATA.GEN_AI_RESPONSE_MODEL, x.model)
Expand All @@ -1063,7 +1079,21 @@ def _wrap_synchronous_completions_chunk_iterator(
if len(data_buf) <= choice_index:
data_buf.append([])
data_buf[choice_index].append(content or "")
if (
hasattr(choice, "finish_reason")
and choice.finish_reason is not None
):
finish_reasons.append(choice.finish_reason)
choice_index += 1
if len(finish_reasons) > 0:
if isinstance(span, StreamedSpan):
span.set_attribute(
SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS, finish_reasons
)
else:
span.set_data(
SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS, finish_reasons
)
if hasattr(x, "usage"):
streaming_message_total_token_usage = x.usage

Expand Down Expand Up @@ -1117,6 +1147,7 @@ async def _wrap_asynchronous_completions_chunk_iterator(
streaming_message_total_token_usage = None
client = sentry_sdk.get_client()

finish_reasons: "list[str]" = []
async for x in old_iterator:
if isinstance(span, StreamedSpan):
span.set_attribute(SPANDATA.GEN_AI_RESPONSE_MODEL, x.model)
Expand All @@ -1134,7 +1165,21 @@ async def _wrap_asynchronous_completions_chunk_iterator(
if len(data_buf) <= choice_index:
data_buf.append([])
data_buf[choice_index].append(content or "")
if (
hasattr(choice, "finish_reason")
and choice.finish_reason is not None
):
finish_reasons.append(choice.finish_reason)
choice_index += 1
if len(finish_reasons) > 0:
if isinstance(span, StreamedSpan):
span.set_attribute(
SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS, finish_reasons
)
else:
span.set_data(
SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS, finish_reasons
)
if hasattr(x, "usage"):
streaming_message_total_token_usage = x.usage

Expand Down
11 changes: 11 additions & 0 deletions tests/integrations/openai/test_openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,7 @@ def test_completions_api_data_collection_outputs(
span_data = span["data"]

assert span_data[SPANDATA.GEN_AI_RESPONSE_MODEL] == "gpt-3.5-turbo"
assert span_data[SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS] == ["stop"]

if expect_output:
assert "the model response" in span_data[SPANDATA.GEN_AI_RESPONSE_TEXT]
Expand Down Expand Up @@ -1027,6 +1028,7 @@ async def test_completions_api_data_collection_outputs_async(
span_data = span["data"]

assert span_data[SPANDATA.GEN_AI_RESPONSE_MODEL] == "gpt-3.5-turbo"
assert span_data[SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS] == ["stop"]

if expect_output:
assert "the model response" in span_data[SPANDATA.GEN_AI_RESPONSE_TEXT]
Expand Down Expand Up @@ -1790,6 +1792,7 @@ def test_streaming_chat_completion_no_prompts(
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9

assert span["attributes"][SPANDATA.GEN_AI_RESPONSE_MODEL] == "model-id"
assert span["attributes"][SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS] == ["stop"]

assert SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS not in span["attributes"]
assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["attributes"]
Expand Down Expand Up @@ -1844,6 +1847,7 @@ def test_streaming_chat_completion_no_prompts(
assert span["data"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9

assert span["data"][SPANDATA.GEN_AI_RESPONSE_MODEL] == "model-id"
assert span["data"][SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS] == ["stop"]

assert SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS not in span["data"]
assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"]
Expand Down Expand Up @@ -2702,6 +2706,7 @@ async def test_streaming_chat_completion_async_no_prompts(
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9

assert span["attributes"][SPANDATA.GEN_AI_RESPONSE_MODEL] == "model-id"
assert span["attributes"][SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS] == ["stop"]

assert SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS not in span["attributes"]
assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["attributes"]
Expand Down Expand Up @@ -2758,6 +2763,7 @@ async def test_streaming_chat_completion_async_no_prompts(
assert span["data"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9

assert span["data"][SPANDATA.GEN_AI_RESPONSE_MODEL] == "model-id"
assert span["data"][SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS] == ["stop"]

assert SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS not in span["data"]
assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"]
Expand Down Expand Up @@ -5017,6 +5023,11 @@ def test_completions_token_usage_manual_output_counting_choices(
sentry_sdk.flush()
(span,) = (item.payload for item in items)

assert span["attributes"][SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS] == [
"stop",
"stop",
"stop",
]
assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 20
assert span["attributes"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 20
if tiktoken_encoding_if_installed():
Expand Down