logic-engine / tests /test_pipeline_exports.py
ghostdrive1's picture
Upload folder using huggingface_hub
116524e verified
Raw
History Blame Contribute Delete
6.77 kB
"""Tests that pipeline composition classes are importable from ace.
Verifies the public API surface for pipeline-first composition.
"""
from __future__ import annotations
from typing import Any
from unittest.mock import MagicMock
import pytest
from ace.core.outputs import (
AgentOutput,
ReflectorOutput,
SkillManagerOutput,
)
from ace.core.skillbook import Skillbook, UpdateBatch, UpdateOperation
# ------------------------------------------------------------------ #
# Mock roles for build_steps() tests
# ------------------------------------------------------------------ #
class MockAgent:
def run(self, *a: Any, **kw: Any) -> AgentOutput:
return AgentOutput(reasoning="r", final_answer="a")
class MockReflector:
def reflect(self, *a: Any, **kw: Any) -> ReflectorOutput:
return ReflectorOutput(
reasoning="r",
correct_approach="a",
key_insight="i",
)
class MockSkillManager:
def update_skills(self, *a: Any, **kw: Any) -> SkillManagerOutput:
return SkillManagerOutput(
update=UpdateBatch(
reasoning="r",
operations=[UpdateOperation(type="ADD", section="learned", issue="c")],
),
)
# ------------------------------------------------------------------ #
# Pipeline primitives are importable from ace
# ------------------------------------------------------------------ #
class TestPipelineExports:
def test_pipeline_class(self):
from ace import Pipeline
assert Pipeline is not None
def test_branch_class(self):
from ace import Branch
assert Branch is not None
def test_merge_strategy(self):
from ace import MergeStrategy
assert MergeStrategy is not None
def test_step_protocol(self):
from ace import StepProtocol
assert StepProtocol is not None
def test_sample_result(self):
from ace import SampleResult
assert SampleResult is not None
# ------------------------------------------------------------------ #
# ACE context types are importable from ace
# ------------------------------------------------------------------ #
class TestContextExports:
def test_ace_step_context(self):
from ace import ACEStepContext
assert ACEStepContext is not None
def test_skillbook_view(self):
from ace import SkillbookView
assert SkillbookView is not None
def test_ace_runner(self):
from ace import ACERunner
assert ACERunner is not None
# ------------------------------------------------------------------ #
# All steps are importable from ace
# ------------------------------------------------------------------ #
class TestStepExports:
@pytest.mark.parametrize(
"name",
[
"AgentStep",
"EvaluateStep",
"ReflectStep",
"UpdateStep",
"DeduplicateStep",
"CheckpointStep",
"LoadTracesStep",
"ExportSkillbookMarkdownStep",
"ObservabilityStep",
"PersistStep",
"learning_tail",
],
)
def test_step_importable(self, name: str):
import ace
assert hasattr(ace, name), f"{name} not in ace"
def test_all_steps_in_dunder_all(self):
import ace
step_names = [
"AgentStep",
"EvaluateStep",
"ReflectStep",
"UpdateStep",
"DeduplicateStep",
"CheckpointStep",
"LoadTracesStep",
"ExportSkillbookMarkdownStep",
"ObservabilityStep",
"PersistStep",
"learning_tail",
]
for name in step_names:
assert name in ace.__all__, f"{name} not in __all__"
# ------------------------------------------------------------------ #
# build_steps() returns expected step types
# ------------------------------------------------------------------ #
class TestBuildSteps:
def test_ace_build_steps(self):
from ace import ACE
from ace.steps import AgentStep, EvaluateStep, ReflectStep
steps = ACE.build_steps(
agent=MockAgent(),
reflector=MockReflector(),
skill_manager=MockSkillManager(),
)
assert isinstance(steps, list)
assert len(steps) >= 4 # Agent, Evaluate, Reflect, Update
assert isinstance(steps[0], AgentStep)
assert isinstance(steps[1], EvaluateStep)
assert isinstance(steps[2], ReflectStep)
def test_trace_analyser_build_steps(self):
from ace import TraceAnalyser
from ace.steps import ReflectStep
steps = TraceAnalyser.build_steps(
reflector=MockReflector(),
skill_manager=MockSkillManager(),
)
assert isinstance(steps, list)
assert len(steps) >= 2 # Reflect, Update
assert isinstance(steps[0], ReflectStep)
def test_ace_from_roles_delegates_to_build_steps(self):
"""from_roles() should produce the same steps as build_steps()."""
from ace import ACE
kwargs = dict(
agent=MockAgent(),
reflector=MockReflector(),
skill_manager=MockSkillManager(),
)
runner = ACE.from_roles(**kwargs)
steps = ACE.build_steps(**kwargs)
# Same number of steps
assert len(runner.pipeline._steps) == len(steps)
# Same step types
for pipe_step, built_step in zip(runner.pipeline._steps, steps):
assert type(pipe_step) is type(built_step)
def test_build_steps_with_extra_steps(self):
from ace import ACE
class DummyStep:
requires = frozenset()
provides = frozenset()
def __call__(self, ctx):
return ctx
steps = ACE.build_steps(
agent=MockAgent(),
reflector=MockReflector(),
skill_manager=MockSkillManager(),
extra_steps=[DummyStep()],
)
assert isinstance(steps[-1], DummyStep)
def test_pipeline_from_build_steps(self):
"""Pipeline constructed from build_steps() should be valid."""
from ace import ACE, Pipeline
steps = ACE.build_steps(
agent=MockAgent(),
reflector=MockReflector(),
skill_manager=MockSkillManager(),
)
pipe = Pipeline(steps)
assert pipe is not None
assert len(pipe._steps) == len(steps)