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.

Both seams below default to None on their coordinators, and the factory passed neither until the parity work found it. The two are not in the same state, and saying so precisely matters:

  • working_memory_recent_turns is wired on the cutover path (symfonic.agent.cutover.bundle) and was unreachable here.
  • spreading_activation is wired on neither path. The cutover bundle passes source and policy to the retrieval coordinator and no activation at all, so this is the first door that opens it.

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) -> 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.

Half a window is refused rather than silently closed, for the reason agent.cutover.completeness.refuse_partial_hydration already gives on the other path: a count with no source wires a window that can never read, and a source with no count wires one that will never be called. Both succeed today and hydrate nothing, which is the silent omission this seam was opened to end.

Raises:

Type Description
MemoryContractError

if exactly one of conversation and a positive recent_turns is supplied.

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,
) -> 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.

    Half a window is refused rather than silently closed, for the reason
    ``agent.cutover.completeness.refuse_partial_hydration`` already gives on
    the other path: a count with no source wires a window that can never read,
    and a source with no count wires one that will never be called. Both
    succeed today and hydrate nothing, which is the silent omission this seam
    was opened to end.

    Raises:
        MemoryContractError: if exactly one of ``conversation`` and a positive
            ``recent_turns`` is supplied.
    """
    if (conversation is None) != (recent_turns <= 0):
        raise MemoryContractError(
            "the conversation window needs a source and a positive "
            f"recent_turns, or neither; got conversation="
            f"{'a source' if conversation is not None else 'None'} and "
            f"recent_turns={recent_turns}. A count with no source reads "
            "nothing, and a source with no count is never called."
        )
    return HydrationCoordinator(
        retrieval=RetrievalCoordinator(
            source=PortCandidateSource(store), activation=activation
        ),
        working=(
            None
            if conversation is None
            else WorkingWindow(source=conversation, recent_turns=recent_turns)
        ),
    )