@@ -447,6 +447,86 @@ def test_child_started_after_parent_end_is_marked_as_app_root(memory_exporter):
447447 assert processor ._span_export_expectation_by_id == {}
448448
449449
450+ def test_litellm_raw_request_with_ended_parent_is_not_marked_as_app_root (
451+ memory_exporter ,
452+ ):
453+ tracer_provider , processor = _create_processor (memory_exporter )
454+ litellm_tracer = tracer_provider .get_tracer ("litellm" )
455+
456+ parent = litellm_tracer .start_span ("litellm_request" )
457+ parent_ctx = trace_api .set_span_in_context (parent )
458+ parent .end ()
459+
460+ raw_request = litellm_tracer .start_span (
461+ "raw_gen_ai_request" ,
462+ context = parent_ctx ,
463+ )
464+ raw_request_ctx = trace_api .set_span_in_context (raw_request )
465+ child = litellm_tracer .start_span ("child" , context = raw_request_ctx )
466+ child .end ()
467+ raw_request .end ()
468+
469+ processor .force_flush ()
470+
471+ spans = _get_spans_by_name (memory_exporter )
472+
473+ assert (
474+ spans ["litellm_request" ].attributes [LangfuseOtelSpanAttributes .IS_APP_ROOT ]
475+ is True
476+ )
477+ assert (
478+ LangfuseOtelSpanAttributes .IS_APP_ROOT
479+ not in spans ["raw_gen_ai_request" ].attributes
480+ )
481+ assert LangfuseOtelSpanAttributes .IS_APP_ROOT not in spans ["child" ].attributes
482+ assert processor ._span_export_expectation_by_id == {}
483+
484+
485+ def test_parentless_litellm_raw_request_is_marked_as_app_root (memory_exporter ):
486+ tracer_provider , processor = _create_processor (memory_exporter )
487+ litellm_tracer = tracer_provider .get_tracer ("litellm" )
488+
489+ with litellm_tracer .start_as_current_span ("raw_gen_ai_request" ):
490+ pass
491+
492+ processor .force_flush ()
493+
494+ spans = _get_spans_by_name (memory_exporter )
495+
496+ assert (
497+ spans ["raw_gen_ai_request" ].attributes [LangfuseOtelSpanAttributes .IS_APP_ROOT ]
498+ is True
499+ )
500+ assert processor ._span_export_expectation_by_id == {}
501+
502+
503+ def test_raw_request_name_from_other_scope_remains_app_root_eligible (
504+ memory_exporter ,
505+ ):
506+ tracer_provider , processor = _create_processor (memory_exporter )
507+ openinference_tracer = tracer_provider .get_tracer ("openinference.custom" )
508+
509+ parent = openinference_tracer .start_span ("parent" )
510+ parent_ctx = trace_api .set_span_in_context (parent )
511+ parent .end ()
512+
513+ child = openinference_tracer .start_span (
514+ "raw_gen_ai_request" ,
515+ context = parent_ctx ,
516+ )
517+ child .end ()
518+
519+ processor .force_flush ()
520+
521+ spans = _get_spans_by_name (memory_exporter )
522+
523+ assert (
524+ spans ["raw_gen_ai_request" ].attributes [LangfuseOtelSpanAttributes .IS_APP_ROOT ]
525+ is True
526+ )
527+ assert processor ._span_export_expectation_by_id == {}
528+
529+
450530def test_set_langfuse_trace_id_in_baggage_sets_value ():
451531 trace_id = "a" * 32
452532 context = _set_langfuse_trace_id_in_baggage (
0 commit comments