Skip to content

Commit 9e25a3b

Browse files
test(galileo): verify factory BaseStep compatibility
1 parent f48ea25 commit 9e25a3b

2 files changed

Lines changed: 63 additions & 1 deletion

File tree

‎evaluators/contrib/galileo/src/agent_control_evaluator_galileo/records/factory.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,10 @@ def record_from_step(
242242
values override only the corresponding input/output fields, preserving the
243243
selected evaluator payload while the unselected side comes from ``step``.
244244
Trace and session records require structured child context; scalar selected
245-
values are never promoted into those record types.
245+
values are never promoted into those record types. The result is a concrete
246+
Galileo Core step model (a ``BaseStep`` subclass) that can be JSON-serialized
247+
and revalidated using that subtype's schema. The factory doesn't assign
248+
execution IDs or storage ownership fields.
246249
"""
247250
if not isinstance(step, Step):
248251
raise RecordFactoryError("A complete Agent Control Step is required.")

‎evaluators/contrib/galileo/tests/test_records_factory.py‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from galileo_core.schemas.logging.llm import Message
2424
from galileo_core.schemas.logging.session import Session
2525
from galileo_core.schemas.logging.span import LlmSpan, RetrieverSpan, ToolSpan
26+
from galileo_core.schemas.logging.step import BaseStep
2627
from galileo_core.schemas.logging.trace import Trace
2728
from galileo_core.schemas.shared.content_parts import FileContentPart, TextContentPart
2829
from galileo_core.schemas.shared.document import Document
@@ -48,6 +49,64 @@ def test_required_galileo_core_public_exports_are_importable() -> None:
4849
assert all((LlmSpan, ToolSpan, RetrieverSpan, Trace, Session, Document, Message))
4950

5051

52+
def test_factory_records_are_serializable_concrete_base_steps() -> None:
53+
# Given: one record of each type accepted by the Galileo factory
54+
records = [
55+
record_from_step(Step(type="llm", name="answer", input="question", output="answer")),
56+
record_from_step(Step(type="tool", name="search", input={"query": "q"}, output="result")),
57+
record_from_step(
58+
Step(type="retriever", name="retrieve", input="question", output=["document"])
59+
),
60+
record_from_step(
61+
Step(
62+
type="trace",
63+
name="request",
64+
input="question",
65+
output="answer",
66+
context={"spans": [{"type": "llm", "name": "answer", "input": "question"}]},
67+
)
68+
),
69+
record_from_step(
70+
Step(
71+
type="session",
72+
name="conversation",
73+
input="question",
74+
context={
75+
"traces": [
76+
{
77+
"type": "trace",
78+
"name": "request",
79+
"input": "question",
80+
"spans": [{"type": "llm", "name": "answer", "input": "question"}],
81+
}
82+
]
83+
},
84+
)
85+
),
86+
]
87+
88+
# When: each concrete Galileo step is serialized and parsed back through its own schema
89+
rebuilt = [
90+
type(record).model_validate(record.model_dump(mode="json", exclude_none=True))
91+
for record in records
92+
]
93+
94+
# Then: every concrete class remains a BaseStep and keeps its discriminator and children
95+
assert all(isinstance(record, BaseStep) for record in records)
96+
assert all(isinstance(record, BaseStep) for record in rebuilt)
97+
assert [record.type.value for record in rebuilt] == [
98+
"llm",
99+
"tool",
100+
"retriever",
101+
"trace",
102+
"session",
103+
]
104+
assert isinstance(rebuilt[3], Trace)
105+
assert isinstance(rebuilt[4], Session)
106+
assert isinstance(rebuilt[3].spans[0], LlmSpan)
107+
assert isinstance(rebuilt[4].traces[0].spans[0], LlmSpan)
108+
109+
51110
def test_llm_messages_use_public_canonical_validation() -> None:
52111
record = record_from_step(
53112
Step(

0 commit comments

Comments
 (0)