symfonic.capabilities.memory.legacy_port¶
legacy_port ¶
The legacy retrieval engine, seen as a MemoryRetrievalPort.
The last adapter #14 needs: it is what lets an engine that already has a memory orchestrator hand the migrated path a port, instead of the migrated path requiring an adopter to wire one by hand.
Everything hard about it was decided in the design and built in
:mod:.admission. This is the assembly:
- legacy
RetrievalEngine.retrieveanswers withMemoryEntryrows; - :func:
~.admission.admit_legacy_entriesresolves each row's provenance and excludes the ones that have none — explicitscope_path, else the record's owntenant_id, else refused; - what survives becomes the
RetrievalResultthe port promises.
The excluded rows are reported in dropped rather than logged and forgotten.
A silently shorter recall is indistinguishable from a scope that genuinely
remembers less, and that is the failure mode this whole task has been avoiding.
LegacyRetrievalPort ¶
Reads the legacy retrieval engine through the capability's port.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
Any
|
the legacy |
required |
scope_of
|
Any
|
turns a capability :class: |
required |
embedding_provider
|
Any
|
what embeds the cue for the vector half of the
search. TA8.73: this was missing, and its absence was silent.
|
None
|
Source code in src/symfonic/capabilities/memory/legacy_port.py
retrieve
async
¶
The public port: capped to query.limit, and scope-checked.
Two obligations this class owes anyone holding it directly, and neither can be delegated to a downstream component that happens to exist in production:
- the cap. A query asking for
limitmemories receives at mostlimit, and the rows admission accepted beyond it are reported indroppedrather than deleted -- a memory removed with no reason is the failure mode this whole adapter keeps being asked to avoid. - visibility. A backend enforces isolation and this verifies it,
because the backend is the component that might be wrong. A row from
another tenant is a
ScopeViolation, not a filtered result: a silent filter would leave the leak in place for the next consumer.
Overscanning is deliberately not here. Fetching wide is a decision
about gates that run after this port, so it belongs to
:meth:scan_candidates, which the coordinator's source uses.
Source code in src/symfonic/capabilities/memory/legacy_port.py
scan_candidates
async
¶
The internal seam: everything admissible within the scan budget.
Uncapped by query.limit on purpose, and separate from
:meth:retrieve rather than a mode of it. The gates that decide what
survives -- scope, salience, relevance, the character caps -- all run
after this call, so a cap here makes one rejection a lost slot. Three
successive attempts to express that as a widened limit on the public
port failed the same way, because a cap is a cap whatever number it
holds.
Visibility is checked here too: the coordinator re-checks scope, but a foreign row should not travel that far, and the check is cheap next to the round trip that produced the row.
Source code in src/symfonic/capabilities/memory/legacy_port.py
first_page ¶
The size of the first read, before any paging.