symfonic.memory.protocols¶
protocols ¶
Protocol interfaces defining the stable public contract.
Architecture: BaseMemoryStore is the UNIFORM protocol. Every memory layer MUST implement all five methods (retrieve, write, summarize, link, delete). Methods that are not meaningful for a given layer return no-op results: - retrieve -> empty list - write -> no-op - summarize -> empty string - link -> no-op - delete -> no-op
Layers also expose layer-specific domain methods (store_fact, query_events, push, set_trigger, etc.) which are the rich API used by MemoryOrchestrator.commit_pending via layer-type dispatch.
Backend protocols (GraphBackend, VectorBackend, EmbeddingProvider) define the storage abstraction that concrete backends implement.
BaseMemoryStore ¶
Bases: Protocol
Uniform protocol that every memory layer must implement.
This is the adapter interface. Layers that do not support a given operation return no-op results rather than raising NotImplementedError.
delete
async
¶
Delete a memory entry/node from this layer.
No-op if deletion is not meaningful for this layer.
link
async
¶
Create a relationship link between two nodes.
No-op if linking is not meaningful for this layer.
render_for_prompt ¶
render_for_prompt(
entry: Any,
scrubber: Callable[[dict[str, Any]], dict[str, Any]]
| None = None,
) -> str
Render a single retrieved entry for the MEMORY_CONTEXT block.
v7.7 (Jarvio procedural-render-bypass follow-up). Each layer
owns its prompt-render contract so the engine's hydrate loop
becomes polymorphic dispatch (one line, no schema-baked key
enumeration). Layers that don't need a layer-specific shape
delegate to :func:symfonic.memory.render.render_legacy --
that helper preserves the pre-v7.7 [layer] content (k=v)
byte-identical output so prompt-cache parity holds for
adopters who haven't opted into any new metadata surface.
scrubber is the agent-bound credential-scrubber callable
(SymfonicAgent._scrub_props). Layers MUST thread it into
any properties rendering so v6.1.9 F2 credential-scrubbing
defense-in-depth holds through every render path. None
when the agent has no credential pattern configured.
Returns the fully-rendered line including the leading
[<layer>] tag. The engine assembles the final
MEMORY_CONTEXT by joining all per-entry lines with \n.
Source code in src/symfonic/memory/protocols.py
retrieve
async
¶
Retrieve memory entries matching the query.
Returns an empty list if retrieval is not meaningful for this layer.
summarize
async
¶
Produce a summary of the given entries.
Returns an empty string if summarization is not meaningful.
write
async
¶
Write a memory entry to this layer.
No-op if writing is not meaningful for this layer.
EmbeddingProvider ¶
GraphBackend ¶
Bases: Protocol
Protocol for graph node and edge persistence.
All methods enforce tenant isolation via TenantScope. Provides 9 core operations for full graph lifecycle management.
add_edge
async
¶
add_node
async
¶
delete_edge
async
¶
delete_node
async
¶
Delete a node. When cascade=True, also removes all connected edges.
get_neighbors
async
¶
Get neighboring nodes, optionally filtered by relationship type.
get_node
async
¶
query_edges
async
¶
query_edges(
scope: TenantScope,
filters: dict[str, Any] | None = None,
limit: int = 50,
offset: int = 0,
) -> list[Any]
Query edges with optional filters and pagination.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scope
|
TenantScope
|
Tenant isolation scope. |
required |
filters
|
dict[str, Any] | None
|
Optional filter dict. Supported key: |
None
|
limit
|
int
|
Maximum number of edges to return. |
50
|
offset
|
int
|
Number of edges to skip (for pagination). |
0
|
Returns:
| Type | Description |
|---|---|
list[Any]
|
List of |
Source code in src/symfonic/memory/protocols.py
query_nodes
async
¶
Query nodes by filters (layer, label, properties, etc.).
limit=None returns all matching nodes (no truncation).
Source code in src/symfonic/memory/protocols.py
traverse
async
¶
update_node
async
¶
Partial update of node properties. Returns the updated node.
upsert_edge
async
¶
Insert edge or increment weight if it already exists.
An edge is considered a duplicate when (tenant_id, source, target, relationship) all match. On conflict the weight is incremented by 1; no duplicate row is created and the edge id is preserved from the first insert.
Source code in src/symfonic/memory/protocols.py
VectorBackend ¶
Bases: Protocol
Protocol for vector storage and similarity search.
add
async
¶
add(
scope: TenantScope,
ids: list[str],
embeddings: list[list[float]],
metadatas: list[dict[str, Any]],
documents: list[str],
) -> None
Add vectors with metadata to the store.
count
async
¶
delete
async
¶
search
async
¶
Search for the top_k most similar vectors.
Returns list of dicts with keys: id, score, metadata, document.