3232 event_from_exception ,
3333 safe_serialize ,
3434)
35- from google .genai .types import GenerateContentConfig
35+ from google .genai .types import GenerateContentConfig , Part , Content
36+ from itertools import chain
3637
3738if TYPE_CHECKING :
3839 from sentry_sdk .tracing import Span
40+ from sentry_sdk ._types import TextPart
3941 from google .genai .types import (
4042 GenerateContentResponse ,
4143 ContentListUnion ,
44+ ContentUnionDict ,
4245 Tool ,
4346 Model ,
4447 EmbedContentResponse ,
48+ ContentUnion ,
4549 )
4650
4751
@@ -720,6 +724,62 @@ def extract_finish_reasons(
720724 return finish_reasons if finish_reasons else None
721725
722726
727+ def _transform_system_instruction_one_level (
728+ system_instructions : "Union[ContentUnionDict, ContentUnion]" ,
729+ can_be_content : bool ,
730+ ) -> "list[TextPart]" :
731+ text_parts : "list[TextPart]" = []
732+
733+ if isinstance (system_instructions , str ):
734+ return [{"type" : "text" , "content" : system_instructions }]
735+
736+ if isinstance (system_instructions , Part ) and system_instructions .text :
737+ return [{"type" : "text" , "content" : system_instructions .text }]
738+
739+ if can_be_content and isinstance (system_instructions , Content ):
740+ if isinstance (system_instructions .parts , list ):
741+ for part in system_instructions .parts :
742+ if isinstance (part .text , str ):
743+ text_parts .append ({"type" : "text" , "content" : part .text })
744+ return text_parts
745+
746+ if isinstance (system_instructions , dict ) and system_instructions .get ("text" ):
747+ return [{"type" : "text" , "content" : system_instructions ["text" ]}]
748+
749+ elif can_be_content and isinstance (system_instructions , dict ):
750+ parts = system_instructions .get ("parts" , [])
751+ for part in parts :
752+ if isinstance (part , Part ) and isinstance (part .text , str ):
753+ text_parts .append ({"type" : "text" , "content" : part .text })
754+ elif isinstance (part , dict ) and isinstance (part .get ("text" ), str ):
755+ text_parts .append ({"type" : "text" , "content" : part ["text" ]})
756+ return text_parts
757+
758+ return text_parts
759+
760+
761+ def _transform_system_instructions (
762+ system_instructions : "Union[ContentUnionDict, ContentUnion]" ,
763+ ) -> "list[TextPart]" :
764+ text_parts : "list[TextPart]" = []
765+
766+ if isinstance (system_instructions , list ):
767+ text_parts = list (
768+ chain .from_iterable (
769+ _transform_system_instruction_one_level (
770+ instructions , can_be_content = False
771+ )
772+ for instructions in system_instructions
773+ )
774+ )
775+
776+ return text_parts
777+
778+ return _transform_system_instruction_one_level (
779+ system_instructions , can_be_content = True
780+ )
781+
782+
723783def set_span_data_for_request (
724784 span : "Span" ,
725785 integration : "Any" ,
@@ -741,27 +801,19 @@ def set_span_data_for_request(
741801 messages = []
742802
743803 # Add system instruction if present
804+ system_instructions = None
744805 if config and hasattr (config , "system_instruction" ):
745- system_instruction = config .system_instruction
746- if system_instruction :
747- system_messages = extract_contents_messages (system_instruction )
748- # System instruction should be a single system message
749- # Extract text from all messages and combine into one system message
750- system_texts = []
751- for msg in system_messages :
752- content = msg .get ("content" )
753- if isinstance (content , list ):
754- # Extract text from content parts
755- for part in content :
756- if isinstance (part , dict ) and part .get ("type" ) == "text" :
757- system_texts .append (part .get ("text" , "" ))
758- elif isinstance (content , str ):
759- system_texts .append (content )
760-
761- if system_texts :
762- messages .append (
763- {"role" : "system" , "content" : " " .join (system_texts )}
764- )
806+ system_instructions = config .system_instruction
807+ elif isinstance (config , dict ) and "system_instruction" in config :
808+ system_instructions = config .get ("system_instruction" )
809+
810+ if system_instructions is not None :
811+ set_data_normalized (
812+ span ,
813+ SPANDATA .GEN_AI_SYSTEM_INSTRUCTIONS ,
814+ _transform_system_instructions (system_instructions ),
815+ unpack = False ,
816+ )
765817
766818 # Extract messages from contents
767819 contents_messages = extract_contents_messages (contents )
0 commit comments