symfonic.capabilities.memory.capability¶
capability ¶
Memory as something Agent can be handed — W2 phase 4.
Everything below hydrate already existed and was tested: the hydration stack has
declared the three stages, with their effects, events and ordering, since T3.2.3
— and nothing ever compiled or ran them. This module is the twenty lines that
hand the declaration and the handler to the kernel at once, the way
PromptingCapability did for prompting.
memory.retrieval and memory.write are wired. POST_MODEL dispatches since
23 slice 1 — kernel/post_model.py, called from runner.py — so the write¶
half is a contribution the kernel actually runs, and TA8.61 made it one.
memory.lifecycle is wired too, since TA8.61 gave FINALIZE a rung
(kernel/finalize.py — not kernel/finalizers.py, which is the teardown
stack, a different thing with a similar name).
It is not optional decoration. MemoryWritePort.write stages a memory as
pending and its own contract says pending memories are not retrievable until
flush, so a deployment that contributed the write stage and not this one
recorded into a void: the turn answered, the store filled, and nothing could
ever be recalled. The two halves are declared together for that reason.
The write stage is declared only when the capability was handed something to write with. Absence means disabled, the same rule the facade applies to capabilities themselves: a deployment that passes no writer gets exactly the contribution it got before.
It resolves; it does not compile. Under the reformulated STG-7 this is a
resolution stage: it reaches the store once per invocation under a
memory-read grant, leaves one entry in the turn's snapshot, and never writes
the assembly. The prompt compiler reads that entry afterwards, purely.
The seam stays vocabulary, not import. What the entry carries is
:func:contribution_spec's mapping — described by its own docstring as
"compiler-ready keyword values", whose keys are the prompt contract's field
names. Prompting consuming that mapping is prompting reading its own vocabulary,
not reaching into memory; and memory never imports prompting to produce it. That
is the same seam contribution.py was written around, used as intended.
MemoryCapability
dataclass
¶
MemoryCapability(hydrator: HydrationCoordinator | None, scope: MemoryScope, cue_from: Callable[[Any], str] = lambda request: str(getattr(request, 'prompt', '') or ''), limit: int = 5, writer: Any | None = None, records_from: Callable[[Any], Any] | None = None, lifecycle: Any | None = None, consolidation: Any | None = None, extractor: Any | None = None, recall_budget: RecallBudget | None = None)
Recall from the memory store and offer it to the turn's prompt.
Usage::
Agent(provider, capabilities=[
GrantEffects("memory-read"),
MemoryCapability(bridge, scope=MemoryScope(tenant="acme")),
PromptingCapability(sources=[...]),
])
The GrantEffects entry is not optional decoration: this capability
declares a memory-read effect, and a capability may not grant itself
one. Constructing the Agent without it fails, loudly, at construction.
contribute ¶
Declare the retrieval stage and the handler that answers it.