@@ -880,12 +880,10 @@ class DocumentModel(BaseModel):
880880def test_llm_tool_definitions_normalize_nested_pydantic_uuid_and_datetime_values () -> None :
881881 identifier = uuid4 ()
882882 timestamp = datetime (2025 , 1 , 2 , tzinfo = UTC )
883- source = LlmSpan (
884- input = "question" ,
885- tools = [{"id" : identifier , "created_at" : timestamp }],
883+ normalized_tools = GalileoRecordNormalizer .llm_tools (
884+ [{"id" : identifier , "created_at" : timestamp }]
886885 )
887- wire_json = source .model_dump (mode = "json" , exclude_none = True )
888- record = LlmSpan .model_validate (wire_json )
886+ record = LlmSpan .model_validate ({"input" : "question" , "tools" : normalized_tools })
889887
890888 assert isinstance (record , LlmSpan )
891889 assert record .tools == [
@@ -1260,6 +1258,23 @@ def test_luna_wire_hierarchy_uses_top_level_fields_not_context() -> None:
12601258 assert rebuilt_trace .spans [0 ].name == "wire tool"
12611259 assert all (span .name != "ignored span" for span in rebuilt_trace .spans )
12621260
1261+ # Galileo Core supplies empty hierarchy defaults when top-level fields are absent.
1262+ # Context-only hierarchy must not be treated as canonical spans or traces.
1263+ context_only_trace = ScorerInvokeRecord .model_validate (
1264+ {
1265+ "type" : "trace" ,
1266+ "name" : "context only trace" ,
1267+ "context" : {
1268+ "children" : [{"type" : "tool" , "name" : "ignored child" }],
1269+ "spans" : [{"type" : "tool" , "name" : "ignored span" }],
1270+ },
1271+ }
1272+ )
1273+ rebuilt_context_only_trace = Trace .model_validate (
1274+ context_only_trace .model_dump (mode = "json" , exclude_none = True )
1275+ )
1276+ assert rebuilt_context_only_trace .spans == []
1277+
12631278 context_only_session = ScorerInvokeRecord .model_validate (
12641279 {
12651280 "type" : "session" ,
@@ -1268,24 +1283,8 @@ def test_luna_wire_hierarchy_uses_top_level_fields_not_context() -> None:
12681283 }
12691284 )
12701285 context_only_json = context_only_session .model_dump (mode = "json" , exclude_none = True )
1271- with pytest .raises (ValidationError ) as missing_spans :
1272- Trace .model_validate (
1273- ScorerInvokeRecord .model_validate (
1274- {
1275- "type" : "trace" ,
1276- "name" : "context only trace" ,
1277- "context" : {
1278- "children" : [{"type" : "tool" , "name" : "ignored child" }],
1279- "spans" : [{"type" : "tool" , "name" : "ignored span" }],
1280- },
1281- }
1282- ).model_dump (mode = "json" , exclude_none = True )
1283- )
1284- assert any (error ["loc" ] == ("spans" ,) for error in missing_spans .value .errors ())
1285-
1286- with pytest .raises (ValidationError ) as missing_traces :
1287- Session .model_validate (context_only_json )
1288- assert any (error ["loc" ] == ("traces" ,) for error in missing_traces .value .errors ())
1286+ rebuilt_context_only_session = Session .model_validate (context_only_json )
1287+ assert rebuilt_context_only_session .traces == []
12891288
12901289
12911290def test_luna_wire_round_trip_preserves_all_trace_span_types_and_nested_tool_span () -> None :
@@ -1337,8 +1336,6 @@ def test_luna_wire_round_trip_preserves_all_trace_span_types_and_nested_tool_spa
13371336@pytest .mark .parametrize (
13381337 ("payload" , "core_model" ),
13391338 [
1340- ({"type" : "trace" , "name" : "trace" , "input" : "question" }, Trace ),
1341- ({"type" : "session" , "name" : "session" }, Session ),
13421339 (
13431340 {
13441341 "type" : "session" ,
@@ -1357,7 +1354,7 @@ def test_luna_wire_round_trip_preserves_all_trace_span_types_and_nested_tool_spa
13571354 ),
13581355 ],
13591356)
1360- def test_luna_wire_records_reject_missing_or_invalid_hierarchy (
1357+ def test_luna_wire_records_reject_invalid_hierarchy (
13611358 payload : dict [str , object ],
13621359 core_model : type [BaseModel ],
13631360) -> None :
0 commit comments