Skip to content

symfonic.capabilities.memory.hydrator_assembly

hydrator_assembly

Assembling one scope's hydrator from the parts a deployment supplied.

Split from :mod:symfonic.capabilities.memory.factory at the 300-line budget, and the two halves are two questions: the factory decides what a memory capability is, and this decides how the recall half of it is put together.

The factory supplies the conversation window and optional activation here. The cutover bundle independently supplies its window but no activation.

Keeping the assembly in one named place is what makes a third such seam visible when it is added rather than quietly skipped.

hydrator_for

hydrator_for(store: Any, *, conversation: Any | None = None, recent_turns: int = 0, activation: Any | None = None, retrieval_policy: RetrievalPolicy | None = None, retrieval: RetrievalCoordinator | None = None, exclude_speakers: frozenset[str] = frozenset()) -> HydrationCoordinator

The recall half of a memory capability, with what was supplied wired in.

A window with no source stays closed rather than being invented: the conversation belongs to the deployment, and a factory that synthesised one would be deciding what the model is told was said.

Zero explicitly disables the working window, retaining ranked recall. A positive count still requires a conversation source.

Raises:

Type Description
MemoryContractError

if a positive count has no conversation source.

Source code in src/symfonic/capabilities/memory/hydrator_assembly.py
def hydrator_for(
    store: Any,
    *,
    conversation: Any | None = None,
    recent_turns: int = 0,
    activation: Any | None = None,
    retrieval_policy: RetrievalPolicy | None = None,
    retrieval: RetrievalCoordinator | None = None,
    exclude_speakers: frozenset[str] = frozenset(),
) -> HydrationCoordinator:
    """The recall half of a memory capability, with what was supplied wired in.

    A window with no source stays closed rather than being invented: the
    conversation belongs to the deployment, and a factory that synthesised one
    would be deciding what the model is told was said.

    Zero explicitly disables the working window, retaining ranked recall.
    A positive count still requires a conversation source.

    Raises:
        MemoryContractError: if a positive count has no conversation source.
    """
    if recent_turns < 0 or (conversation is None and recent_turns > 0):
        raise MemoryContractError(
            "the conversation window needs a source for positive "
            f"recent_turns and rejects negative counts; got conversation="
            f"{'a source' if conversation is not None else 'None'} and "
            f"recent_turns={recent_turns}. Zero disables the window."
        )
    if retrieval is not None and (retrieval_policy is not None or activation is not None):
        raise MemoryContractError(
            "a supplied retrieval coordinator already owns its policy and activation; "
            "do not also pass retrieval_policy or activation"
        )
    if retrieval_policy is not None and not isinstance(retrieval_policy, RetrievalPolicy):
        raise MemoryContractError("retrieval_policy must be a RetrievalPolicy")
    if retrieval is not None and not isinstance(retrieval, RetrievalCoordinator):
        raise MemoryContractError("retrieval must be a RetrievalCoordinator")
    return HydrationCoordinator(
        retrieval=(retrieval or RetrievalCoordinator(
            source=PortCandidateSource(store), policy=retrieval_policy, activation=activation
        )).excluding_speakers(exclude_speakers),
        working=(
            None
            if conversation is None
            else WorkingWindow(
                source=conversation,
                recent_turns=recent_turns,
                exclude_speakers=exclude_speakers,
            )
        ),
    )