logic-engine / ace /steps /reflect.py
ghostdrive1's picture
Upload folder using huggingface_hub
116524e verified
Raw
History Blame Contribute Delete
3.16 kB
"""ReflectStep β€” analyses a trace to produce a ReflectorOutput."""
from __future__ import annotations
import logging
from ..core.context import ACEStepContext
from ..core.outputs import AgentOutput
from ..protocols import ReflectorLike
logger = logging.getLogger(__name__)
class ReflectStep:
"""Run the Reflector role against the trace and current skillbook.
Receives ``ctx.trace`` β€” a dict from EvaluateStep (standard ACE pipeline),
a raw object from TraceAnalyser, or any integration-produced trace. When
the trace is a dict with known keys, the step extracts them and calls the
Reflector's existing API. For raw/opaque traces, it passes them as
keyword arguments for the Reflector to handle.
Declares ``async_boundary = True`` β€” everything from this step onward
runs in a background thread pool when the pipeline has background
execution enabled.
Pure β€” produces a reflection object, no side effects.
"""
requires = frozenset({"trace", "skillbook"})
provides = frozenset({"reflections"})
async_boundary = True
max_workers = 3
def __init__(self, reflector: ReflectorLike) -> None:
self.reflector = reflector
@staticmethod
def _is_batch_container(trace: dict) -> bool:
for key in ("items", "tasks"):
if isinstance(trace.get(key), list):
return True
steps = trace.get("steps")
return (
isinstance(steps, list)
and bool(steps)
and all(
isinstance(step, dict)
and step.get("role") == "conversation"
and isinstance(step.get("content"), dict)
for step in steps
)
)
def __call__(self, ctx: ACEStepContext) -> ACEStepContext:
trace = ctx.trace
if isinstance(trace, dict) and not self._is_batch_container(trace):
# Structured trace from EvaluateStep β€” extract known fields
agent_output = AgentOutput(
reasoning=trace.get("reasoning", ""),
final_answer=trace.get("answer", ""),
)
reflection = self.reflector.reflect(
question=trace.get("question", ""),
agent_output=agent_output,
skillbook=ctx.skillbook,
ground_truth=trace.get("ground_truth"),
feedback=trace.get("feedback"),
injected_skill_ids=ctx.injected_skill_ids,
mode=ctx.mode,
)
else:
# Raw trace from TraceAnalyser or integration β€” pass as-is
# The Reflector must handle the trace type via **kwargs
reflection = self.reflector.reflect(
question="",
agent_output=AgentOutput(reasoning="", final_answer=""),
skillbook=ctx.skillbook,
trace=trace,
injected_skill_ids=ctx.injected_skill_ids,
mode=ctx.mode,
)
return ctx.replace(reflections=(reflection,))