@@ -501,6 +501,82 @@ def test_parent_child_span_relationship(self):
501501 # Parent should not have a parent (root)
502502 assert parent_span .parent is None
503503
504+ @patch_env_vars (
505+ stability_mode = "gen_ai_latest_experimental" ,
506+ content_capturing = "SPAN_ONLY" ,
507+ emit_event = "" ,
508+ )
509+ def test_embedding_parent_child_span_relationship (self ):
510+ parent_invocation = EmbeddingInvocation (
511+ request_model = "embed-parent-model" ,
512+ provider = "test-provider" ,
513+ input_tokens = 10 ,
514+ )
515+ child_invocation = EmbeddingInvocation (
516+ request_model = "embed-child-model" ,
517+ provider = "test-provider" ,
518+ input_tokens = 5 ,
519+ )
520+
521+ self .telemetry_handler .start_embedding (parent_invocation )
522+ assert parent_invocation .span is not None
523+ self .telemetry_handler .start_embedding (child_invocation )
524+ assert child_invocation .span is not None
525+ self .telemetry_handler .stop_embedding (child_invocation )
526+ self .telemetry_handler .stop_embedding (parent_invocation )
527+
528+ spans = self .span_exporter .get_finished_spans ()
529+ assert len (spans ) == 2
530+ child_span = next (
531+ s for s in spans if s .name == "embeddings embed-child-model"
532+ )
533+ parent_span = next (
534+ s for s in spans if s .name == "embeddings embed-parent-model"
535+ )
536+
537+ assert child_span .context .trace_id == parent_span .context .trace_id
538+ assert child_span .parent is not None
539+ assert child_span .parent .span_id == parent_span .context .span_id
540+ assert parent_span .parent is None
541+
542+ @patch_env_vars (
543+ stability_mode = "gen_ai_latest_experimental" ,
544+ content_capturing = "SPAN_ONLY" ,
545+ emit_event = "" ,
546+ )
547+ def test_llm_parent_embedding_child_span_relationship (self ):
548+ message = _create_input_message ("hi" )
549+ chat_generation = _create_output_message ("ok" )
550+ child_invocation = EmbeddingInvocation (
551+ request_model = "embed-child-model" ,
552+ provider = "test-provider" ,
553+ input_tokens = 3 ,
554+ )
555+
556+ with self .telemetry_handler .llm () as parent_invocation :
557+ for attr , value in {
558+ "request_model" : "parent-model" ,
559+ "input_messages" : [message ],
560+ "provider" : "test-provider" ,
561+ }.items ():
562+ setattr (parent_invocation , attr , value )
563+ self .telemetry_handler .start_embedding (child_invocation )
564+ assert child_invocation .span is not None
565+ self .telemetry_handler .stop_embedding (child_invocation )
566+ parent_invocation .output_messages = [chat_generation ]
567+
568+ spans = self .span_exporter .get_finished_spans ()
569+ assert len (spans ) == 2
570+ child_span = next (
571+ s for s in spans if s .name == "embeddings embed-child-model"
572+ )
573+ parent_span = next (s for s in spans if s .name == "chat parent-model" )
574+
575+ assert child_span .context .trace_id == parent_span .context .trace_id
576+ assert child_span .parent is not None
577+ assert child_span .parent .span_id == parent_span .context .span_id
578+ assert parent_span .parent is None
579+
504580 def test_llm_context_manager_error_path_records_error_status_and_attrs (
505581 self ,
506582 ):
@@ -907,92 +983,6 @@ def test_embedding_manual_start_and_stop_creates_span(self):
907983 },
908984 )
909985
910- @patch_env_vars (
911- stability_mode = "gen_ai_latest_experimental" ,
912- content_capturing = "EVENT_ONLY" ,
913- emit_event = "true" ,
914- )
915- def test_emits_embedding_event (self ):
916- invocation = EmbeddingInvocation (
917- request_model = "event-embed-model" ,
918- provider = "test-provider" ,
919- dimension_count = 1024 ,
920- encoding_formats = ["float" ],
921- input_tokens = 10 ,
922- server_address = "event.server.com" ,
923- server_port = 8443 ,
924- )
925-
926- self .telemetry_handler .start_embedding (invocation )
927- self .telemetry_handler .stop_embedding (invocation )
928-
929- logs = self .log_exporter .get_finished_logs ()
930- self .assertEqual (len (logs ), 1 )
931- log_record = logs [0 ].log_record
932- self .assertEqual (
933- log_record .event_name , "gen_ai.client.embedding.operation.details"
934- )
935-
936- attrs = log_record .attributes
937- self .assertIsNotNone (attrs )
938- self .assertEqual (attrs [GenAI .GEN_AI_OPERATION_NAME ], "embeddings" )
939- self .assertEqual (
940- attrs [GenAI .GEN_AI_REQUEST_MODEL ], "event-embed-model"
941- )
942- self .assertEqual (attrs [GenAI .GEN_AI_PROVIDER_NAME ], "test-provider" )
943- self .assertEqual (attrs [GenAI .GEN_AI_EMBEDDING_DIMENSION_COUNT ], 1024 )
944- self .assertEqual (
945- _normalize_to_list (attrs [GenAI .GEN_AI_REQUEST_ENCODING_FORMATS ]),
946- ["float" ],
947- )
948- self .assertEqual (attrs [GenAI .GEN_AI_USAGE_INPUT_TOKENS ], 10 )
949- self .assertEqual (attrs [server_attributes .SERVER_ADDRESS ], "event.server.com" )
950- self .assertEqual (attrs [server_attributes .SERVER_PORT ], 8443 )
951-
952- span = _get_single_span (self .span_exporter )
953- self .assertIsNotNone (log_record .trace_id )
954- self .assertIsNotNone (log_record .span_id )
955- self .assertIsNotNone (span .context )
956- self .assertEqual (log_record .trace_id , span .context .trace_id )
957- self .assertEqual (log_record .span_id , span .context .span_id )
958-
959- @patch_env_vars (
960- stability_mode = "gen_ai_latest_experimental" ,
961- content_capturing = "EVENT_ONLY" ,
962- emit_event = "true" ,
963- )
964- def test_emits_embedding_event_with_error (self ):
965- class EmbeddingError (RuntimeError ):
966- pass
967-
968- invocation = EmbeddingInvocation (
969- request_model = "error-embed-model" ,
970- provider = "test-provider" ,
971- input_tokens = 9 ,
972- )
973- self .telemetry_handler .start_embedding (invocation )
974- error = Error (message = "embedding error" , type = EmbeddingError )
975- self .telemetry_handler .fail_embedding (invocation , error )
976-
977- logs = self .log_exporter .get_finished_logs ()
978- self .assertEqual (len (logs ), 1 )
979- log_record = logs [0 ].log_record
980- attrs = log_record .attributes
981- self .assertEqual (
982- attrs [error_attributes .ERROR_TYPE ], EmbeddingError .__qualname__
983- )
984- self .assertEqual (attrs [GenAI .GEN_AI_OPERATION_NAME ], "embeddings" )
985- self .assertEqual (
986- attrs [GenAI .GEN_AI_REQUEST_MODEL ], "error-embed-model"
987- )
988-
989- span = _get_single_span (self .span_exporter )
990- self .assertIsNotNone (log_record .trace_id )
991- self .assertIsNotNone (log_record .span_id )
992- self .assertIsNotNone (span .context )
993- self .assertEqual (log_record .trace_id , span .context .trace_id )
994- self .assertEqual (log_record .span_id , span .context .span_id )
995-
996986
997987class AnyNonNone :
998988 def __eq__ (self , other ):
0 commit comments