logic-engine / ace /core /context.py
ghostdrive1's picture
Upload folder using huggingface_hub
116524e verified
Raw
History Blame Contribute Delete
4.48 kB
"""Core types for the ACE pipeline: ACESample, SkillbookView, ACEStepContext."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Iterator, Literal, Protocol, runtime_checkable
from pipeline import StepContext
from .outputs import AgentOutput, ReflectorOutput
from .skillbook import Skill, Skillbook, UpdateBatch
# ---------------------------------------------------------------------------
# ACESample β€” structural protocol for step access
# ---------------------------------------------------------------------------
@runtime_checkable
class ACESample(Protocol):
"""Minimal interface that Sample satisfies.
Steps access ``ctx.sample.question`` uniformly. This protocol makes
the duck typing explicit and type-safe β€” ``Sample`` satisfies it
structurally without inheriting from it.
"""
@property
def question(self) -> str: ...
@property
def context(self) -> str: ...
@property
def ground_truth(self) -> str | None: ...
@property
def metadata(self) -> dict: ...
# ---------------------------------------------------------------------------
# SkillbookView β€” read-only projection
# ---------------------------------------------------------------------------
class SkillbookView:
"""Read-only projection of a Skillbook.
Wraps a ``Skillbook`` and exposes only read methods. Write methods
don't exist on this class β€” calling them raises ``AttributeError``
at runtime and a type error at check time.
Safe to place on a frozen ``ACEStepContext``. Steps that need to
write to the skillbook receive the real ``Skillbook`` via constructor
injection.
"""
__slots__ = ("_sb",)
def __init__(self, skillbook: Skillbook) -> None:
self._sb = skillbook
# -- Read methods delegated to the underlying Skillbook --
def as_prompt(self) -> str:
"""Return the markdown-formatted skillbook for LLM consumption."""
return self._sb.as_prompt()
def get_skill(self, skill_id: str) -> Skill | None:
"""Look up a skill by ID."""
return self._sb.get_skill(skill_id)
def skills(self, include_invalid: bool = False) -> list[Skill]:
"""Return all active skills (or all including invalid)."""
return self._sb.skills(include_invalid=include_invalid)
def stats(self) -> dict[str, object]:
"""Return skillbook statistics."""
return self._sb.stats()
def __len__(self) -> int:
return len(self._sb.skills())
def __iter__(self) -> Iterator[Skill]:
return iter(self._sb.skills())
def __repr__(self) -> str:
return f"SkillbookView({len(self)} skills)"
# ---------------------------------------------------------------------------
# ACEStepContext β€” immutable context for the ACE pipeline
# ---------------------------------------------------------------------------
@dataclass(frozen=True)
class ACEStepContext(StepContext):
"""Immutable context carrying all step-to-step data for the ACE pipeline.
The pipeline engine only knows about ``sample`` and ``metadata``; all
ACE-specific fields live here.
The ``skillbook`` field is a ``SkillbookView`` (read-only). Steps that
need to write to the skillbook receive the real ``Skillbook`` via
constructor injection.
The ``trace`` field holds the raw execution record from any external
system β€” a browser-use ``AgentHistoryList``, a LangChain result dict,
a Claude Code transcript, or any arbitrary Python object. It has no
enforced schema. The Reflector receives the raw trace and is
responsible for making sense of it.
"""
# -- Mode --
mode: Literal["online", "offline"] = "online"
# -- Domain fields --
skillbook: SkillbookView | None = None
trace: object | None = None
agent_output: AgentOutput | None = None
reflections: tuple[ReflectorOutput, ...] = ()
skill_manager_output: UpdateBatch | None = None
# Skills rendered into the Agent's prompt this run. Downstream roles
# (Reflector/RR, SkillManager) use this as attribution scope.
injected_skill_ids: tuple[str, ...] = ()
# -- Progress tracking --
epoch: int = 1
total_epochs: int = 1
step_index: int = 0
total_steps: int | None = None
global_sample_index: int = 0