diff --git a/sentry_sdk/integrations/openai.py b/sentry_sdk/integrations/openai.py index 863f146a51..cfd36b84dd 100644 --- a/sentry_sdk/integrations/openai.py +++ b/sentry_sdk/integrations/openai.py @@ -371,13 +371,6 @@ def _set_completions_api_input_data( _commmon_set_input_data(span, kwargs) return - system_instructions = _get_system_instructions_completions(messages) - if len(system_instructions) > 0: - span.set_data( - SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS, - json.dumps(_transform_system_instructions(system_instructions)), - ) - if isinstance(messages, str): normalized_messages = normalize_message_roles([messages]) # type: ignore scope = sentry_sdk.get_current_scope() @@ -390,13 +383,29 @@ def _set_completions_api_input_data( _commmon_set_input_data(span, kwargs) return + # dict special case following https://github.com/openai/openai-python/blob/3e0c05b84a2056870abf3bd6a5e7849020209cc3/src/openai/_utils/_transform.py#L194-L197 + if not isinstance(messages, Iterable) or isinstance(messages, dict): + set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "chat") + _commmon_set_input_data(span, kwargs) + return + + messages = list(messages) + kwargs["messages"] = messages + + system_instructions = _get_system_instructions_completions(messages) + if len(system_instructions) > 0: + span.set_data( + SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS, + json.dumps(_transform_system_instructions(system_instructions)), + ) + 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 + normalized_messages = normalize_message_roles(non_system_messages) scope = sentry_sdk.get_current_scope() messages_data = truncate_and_annotate_messages(normalized_messages, span, scope) if messages_data is not None: diff --git a/tests/integrations/openai/test_openai.py b/tests/integrations/openai/test_openai.py index 094b659b2c..1202c50373 100644 --- a/tests/integrations/openai/test_openai.py +++ b/tests/integrations/openai/test_openai.py @@ -204,6 +204,21 @@ def test_nonstreaming_chat_completion_no_prompts( ], id="parts", ), + pytest.param( + iter( + [ + { + "role": "system", + "content": [ + {"type": "text", "text": "You are a helpful assistant."}, + {"type": "text", "text": "Be concise and clear."}, + ], + }, + {"role": "user", "content": "hello"}, + ] + ), + id="iterator", + ), ], ) def test_nonstreaming_chat_completion(sentry_init, capture_events, messages, request): @@ -335,6 +350,21 @@ async def test_nonstreaming_chat_completion_async_no_prompts( ], id="parts", ), + pytest.param( + iter( + [ + { + "role": "system", + "content": [ + {"type": "text", "text": "You are a helpful assistant."}, + {"type": "text", "text": "Be concise and clear."}, + ], + }, + {"role": "user", "content": "hello"}, + ] + ), + id="iterator", + ), ], ) async def test_nonstreaming_chat_completion_async( @@ -521,6 +551,21 @@ def test_streaming_chat_completion_no_prompts( ], id="parts", ), + pytest.param( + iter( + [ + { + "role": "system", + "content": [ + {"type": "text", "text": "You are a helpful assistant."}, + {"type": "text", "text": "Be concise and clear."}, + ], + }, + {"role": "user", "content": "hello"}, + ] + ), + id="iterator", + ), ], ) def test_streaming_chat_completion(sentry_init, capture_events, messages, request): @@ -757,6 +802,21 @@ async def test_streaming_chat_completion_async_no_prompts( ], id="parts", ), + pytest.param( + iter( + [ + { + "role": "system", + "content": [ + {"type": "text", "text": "You are a helpful assistant."}, + {"type": "text", "text": "Be concise and clear."}, + ], + }, + {"role": "user", "content": "hello"}, + ] + ), + id="iterator", + ), ], ) async def test_streaming_chat_completion_async(