symfonic.memory.backends.postgres_graph¶
postgres_graph ¶
PostgreSQL graph backend using JSONB storage.
Implements GraphBackend protocol with asyncpg for all database I/O.
All operations enforce tenant isolation via WHERE tenant_id = $N.
asyncpg must be installed.
pip install symfonic-core[postgres]
PostgresGraphBackend ¶
GraphBackend backed by PostgreSQL with JSONB storage.
All operations enforce tenant isolation via WHERE tenant_id = $N.
Source code in src/symfonic/memory/backends/postgres_graph.py
backfill_scope_paths
async
¶
Eagerly backfill NULL scope_path rows (design §6.b migration).
Pre-v8.0 rows have scope_path IS NULL; they read as a 1-level root
path tenant\x1f<tenant_id> via the query-time dual-read, so this
eager backfill is OPTIONAL (online-safe, idempotent) — it just
materialises the same value into the column so the B-tree index and
the scorer's dual-read agree. Returns the number of rows updated.
Uses a parameterised expression so adopter tenant_ids cannot inject.
Source code in src/symfonic/memory/backends/postgres_graph.py
ensure_schema
async
¶
Create graph tables (nodes + edges) and their indexes.
Does NOT create the memory_vectors table — that is the
responsibility of :class:PostgresVectorBackend.ensure_schema.
Source code in src/symfonic/memory/backends/postgres_graph.py
query_edges
async
¶
query_edges(
scope: TenantScope,
filters: dict[str, Any] | None = None,
limit: int = 50,
offset: int = 0,
) -> list[MemoryEdge]
Query edges directly from memory_edges table with pagination.
Supports optional relationship filter. All other keys in filters
are ignored to avoid SQL injection risk.
Source code in src/symfonic/memory/backends/postgres_graph.py
traverse
async
¶
BFS graph traversal up to max_depth hops from start node.
Source code in src/symfonic/memory/backends/postgres_graph.py
upsert_edge
async
¶
Insert edge or increment weight by 1 if the same edge already exists.
Uniqueness is determined by (tenant_id, source, target, relationship).
The unique index idx_memory_edges_upsert must exist for ON CONFLICT
to resolve correctly (created by ensure_schema).
Source code in src/symfonic/memory/backends/postgres_graph.py
matches_extra ¶
Apply arbitrary property filters not handled by SQL.
Source code in src/symfonic/memory/backends/postgres_graph.py
row_to_node ¶
Convert an asyncpg Record to a MemoryNode.
v6.2 T02 note: spreading_access_count has no dedicated column
(no schema migration was required by the v6.2 carve-out). It is
round-tripped through the properties JSONB under the reserved
key __spreading_access_count; on read we lift the field out of
properties so the Python model remains clean, and on write
_node_properties_jsonb serialises it back in.
v7.2.1 hotfix: the embedding pgvector column is now hydrated
onto MemoryNode.embedding. Prior versions silently dropped the
column on read, which collapsed RetrievalScorer.semantic_similarity
(weight 0.4) to zero for every Postgres-loaded graph candidate.