Skip to content

symfonic.capabilities.memory.bundle

bundle

Memory as something a host can compose, from a store and a scope.

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 what a host takes away -- the capability and the grants it needs, as one list to compose.

memory_capabilities

memory_capabilities(store: Any, scope: MemoryScope | Any, *, limit: int = 5, recall_budget: RecallBudget | None = None, extractor: MemoryExtractorPort | None = None, summary: STMSummaryPort | None = None, reflection: ReflectionPort | None = None, consolidation: Any | None = None, 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(), episodic_telemetry_sink: EpisodicTelemetrySink | None = None, operation_journal: MemoryOperationJournalPort | None = None, triage: IntentTriageCapability | None = None) -> list[Any]

The capability and the grants it needs, as one list to compose.

Parameters:

Name Type Description Default
store Any

an HMS satisfying the retrieval, write and lifecycle ports.

required
scope MemoryScope | Any

typed paths retain every native kind/ID level. Explicit MemoryScope values retain their version, including positional v0.

required
limit int

how many memories a turn recalls.

5
recall_budget RecallBudget | None

explicit UTF-8 rendered recall ceilings. None preserves legacy character caps; unrelated to the working conversation window.

None
conversation Any | None

an optional conversation source. NativeConversationHistory also installs final-only publication when supplied.

None
recent_turns int

number of recent utterances; zero skips the window read.

0
extractor MemoryExtractorPort | None

a :class:~.ports.MemoryExtractorPort -- normally :class:~.extraction.MemoryExtractionService -- run after the final model round so a turn's durable facts are written without the deployment invoking anything. None composes memory that recalls and records the exchange and extracts nothing.

None
summary STMSummaryPort | None

an explicit evicted-turn summary collaborator, normally :class:~.summary.WorkingMemorySummaryService. It supplies a JIT-only prompt contribution. None disables it without reading a working source.

None
reflection ReflectionPort | None

a completed-exchange insight collaborator. None disables it without calling its collaborator.

None
consolidation Any | None

a :class:~.napping.ConsolidationCoordinator -- built over a :class:~.consolidation.ConsolidationRuntime whose roster came from :func:~.phases.quick.quick_phases. Given one, the turn's last act is to advance this scope's cadence and run the cycle it makes due, in the background. None composes memory that never consolidates on its own.

None
activation Any | None

a :class:~.recall.SpreadingActivation over an :class:~.recall.AssociationSource. It adds bounded graph neighbours to direct recall. None preserves direct recall.

None
retrieval_policy RetrievalPolicy | None

the public relevance, salience, layer, and hierarchy policy for the default coordinator. A policy is applied before the block is compiled; changing a floor or blend changes the recalled records, not merely a diagnostic.

None
retrieval RetrievalCoordinator | None

a fully composed :class:~.RetrievalCoordinator. Use this with an adopter CandidateSource when the store's pre-ranked retrieval is not the desired scorer. It owns policy and activation, so passing either alongside it is refused.

None
exclude_speakers frozenset[str]

transcript and explicitly tagged recalled speakers omitted. Untagged recalled records remain eligible; speaker is never inferred.

frozenset()
episodic_telemetry_sink EpisodicTelemetrySink | None

optional async observer for successful memory.lifecycle publications. It receives scope and run correlation identifiers plus committed record ids, never record text, prompts, responses, or metadata. Sink failures are logged and do not roll back a publication; None is disabled.

None
triage IntentTriageCapability | None

the composed IntentTriageCapability (from symfonic.capabilities.triage) retrieval routes on. Pass the same instance composed into the agent: pure-action turns skip the store round trip. None -- the default -- hydrates every turn.

None

Raises:

Type Description
ConfigurationError

if extractor, summary, or reflection cannot serve its port.

Returned together because a capability may not grant itself an effect and a caller that forgot one would get a fold refusal naming a grant rather than a missing feature. The three are what memory is.

Prompting is NOT included. Memory is a resolution stage: it reaches the store and leaves an entry in the turn's snapshot. Prompting is the compilation stage that reads that snapshot and renders it. Fold memory by itself and both ports are called, the block is composed, the snapshot is populated -- and the model receives the bare instructions.

It is not added here because a factory named for memory that quietly composed prompting would decide a deployment's prompt on its behalf. A composition root wanting recall in the prompt adds a PromptingCapability beside this.

Source code in src/symfonic/capabilities/memory/bundle.py
def memory_capabilities(
    store: Any,
    scope: MemoryScope | Any,
    *,
    limit: int = 5,
    recall_budget: RecallBudget | None = None,
    extractor: MemoryExtractorPort | None = None,
    summary: STMSummaryPort | None = None,
    reflection: ReflectionPort | None = None,
    consolidation: Any | None = None,
    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(),
    episodic_telemetry_sink: EpisodicTelemetrySink | None = None,
    operation_journal: MemoryOperationJournalPort | None = None,
    triage: IntentTriageCapability | None = None,
) -> list[Any]:
    """The capability *and* the grants it needs, as one list to compose.

    Args:
        store: an HMS satisfying the retrieval, write and lifecycle ports.
        scope: typed paths retain every native kind/ID level. Explicit
            MemoryScope values retain their version, including positional v0.
        limit: how many memories a turn recalls.
        recall_budget: explicit UTF-8 rendered recall ceilings. None preserves
            legacy character caps; unrelated to the working conversation window.
        conversation: an optional conversation source. NativeConversationHistory
            also installs final-only publication when supplied.
        recent_turns: number of recent utterances; zero skips the window read.
        extractor: a :class:`~.ports.MemoryExtractorPort` -- normally
            :class:`~.extraction.MemoryExtractionService` -- run after the
            final model round so a turn's durable facts are written without
            the deployment invoking anything. ``None`` composes memory that
            recalls and records the exchange and extracts nothing.
        summary: an explicit evicted-turn summary collaborator, normally
            :class:`~.summary.WorkingMemorySummaryService`. It supplies a
            JIT-only prompt contribution. ``None`` disables it without reading
            a working source.
        reflection: a completed-exchange insight collaborator. ``None``
            disables it without calling its collaborator.
        consolidation: a :class:`~.napping.ConsolidationCoordinator` -- built
            over a :class:`~.consolidation.ConsolidationRuntime` whose roster
            came from :func:`~.phases.quick.quick_phases`. Given one, the
            turn's last act is to advance this scope's cadence and run the
            cycle it makes due, in the background. ``None`` composes memory
            that never consolidates on its own.
        activation: a :class:`~.recall.SpreadingActivation` over an
            :class:`~.recall.AssociationSource`. It adds bounded graph
            neighbours to direct recall. ``None`` preserves direct recall.
        retrieval_policy: the public relevance, salience, layer, and hierarchy
            policy for the default coordinator. A policy is applied before the
            block is compiled; changing a floor or blend changes the recalled
            records, not merely a diagnostic.
        retrieval: a fully composed :class:`~.RetrievalCoordinator`. Use this
            with an adopter ``CandidateSource`` when the store's pre-ranked
            retrieval is not the desired scorer. It owns policy and activation,
            so passing either alongside it is refused.
        exclude_speakers: transcript and explicitly tagged recalled speakers omitted.
            Untagged recalled records remain eligible; speaker is never inferred.
        episodic_telemetry_sink: optional async observer for successful
            ``memory.lifecycle`` publications.  It receives scope and run
            correlation identifiers plus committed record ids, never record
            text, prompts, responses, or metadata.  Sink failures are logged
            and do not roll back a publication; ``None`` is disabled.
        triage: the composed ``IntentTriageCapability`` (from
            ``symfonic.capabilities.triage``) retrieval routes on. Pass the
            same instance composed into the agent: pure-action turns skip the
            store round trip. ``None`` -- the default -- hydrates every turn.

    Raises:
        ConfigurationError: if ``extractor``, ``summary``, or ``reflection``
            cannot serve its port.

    Returned together because a capability may not grant itself an effect and
    a caller that forgot one would get a fold refusal naming a grant rather
    than a missing feature. The three are what memory is.

    **Prompting is NOT included.** Memory is a *resolution* stage: it reaches the
    store and leaves an entry in the turn's snapshot. Prompting is the
    *compilation* stage that reads that snapshot and renders it. Fold memory by
    itself and both ports are called, the block is composed, the snapshot is
    populated -- and the model receives the bare instructions.

    It is not added here because a factory named for memory that quietly
    composed prompting would decide a deployment's prompt on its behalf. A
    composition root wanting recall in the prompt adds a ``PromptingCapability``
    beside this."""
    from symfonic.kernel.contracts.effects import GrantEffects

    # ``memory-consolidate`` travels with the rest for the same reason the
    # other three do: it is granted only when a nap is composed, and a caller
    # who passed a coordinator and no grant would read a fold refusal naming an
    # effect rather than the feature they asked for.
    effects = ["memory-read", "memory-write", "memory-flush"]
    from symfonic.capabilities.memory.turn_generation import bind_turn_generations, history_stage
    conversation, generations = bind_turn_generations(store, scope, conversation)
    if consolidation is not None:
        effects.append("memory-consolidate")
    capabilities = [
        GrantEffects(*effects),
        MemoryBundleFactory(
            store, limit=limit, recall_budget=recall_budget,
            extractor=extractor, summary=summary, reflection=reflection,
            consolidation=consolidation,
            conversation=conversation, recent_turns=recent_turns,
            activation=activation, retrieval_policy=retrieval_policy,
            retrieval=retrieval, exclude_speakers=exclude_speakers,
            episodic_telemetry_sink=episodic_telemetry_sink,
            operation_journal=operation_journal, generations=generations,
            triage=triage,
        ).for_scope(scope),
    ]
    return [*capabilities, *history_stage(conversation, generations, composition_scope(scope))]