zettelkasten.synapse.retrieval¶
zettelkasten.synapse.retrieval ¶
Unified cross-store retrieval: memory tree + intersecting ZK projects.
Everything here reuses the ZK framing machinery (:func:zettelkasten.framing.
project_query / :func:~zettelkasten.framing.frame_question) unchanged — the
only new ingredient is a COMBINED grapher that routes the special _memory
source to :func:zettelkasten.synapse.memory_source.get_memory_source and every
other name to the caller's ZK grapher. Hits are then tagged by store
(memory vs zk) and tier (practice vs canon) so a consumer can
weight private practice against public canon.
Read-only: no store is mutated. The scope is the intersection config (which ZK projects intersect this repo's memory), overridable per call.
clear_federated_cache ¶
Drop the process-global federated graph + peer-ANN caches (test/maintenance hook).
scope_tokens ¶
Round-trippable scope tokens for reporting/manifest (local + federated).
Normalizes the scope arg (config or override) to project /
<repo_id>:<project> tokens that re-parse to the same specs.
Source code in zettelkasten/synapse/retrieval.py
unresolved_federated_repos ¶
Repo ids that ARE in the current scope but cannot be resolved right now.
A federated scope whose peer repo is unreachable via
:func:zettelkasten.federation.find_repo (deleted, moved, or otherwise
offline) — as distinct from a peer the user has intentionally removed from
the scope, which simply isn't among the specs at all. The distinction is what
lets the overlay builder PRESERVE existing edges to a temporarily-missing peer
(rather than pruning expensive LLM-typed edges) while still dropping edges to a
peer that was deliberately dropped from intersects.
Source code in zettelkasten/synapse/retrieval.py
resolve_source_dir ¶
Filesystem dir backing a (possibly namespaced) ZK source name.
A federated <repo_id>:<graph> resolves to <peer .zettelkasten/>/<graph>;
a bare name to default_base/<name>. Used for freshness/staleness scans that
must reach the peer's notes, not a same-named local box.
Source code in zettelkasten/synapse/retrieval.py
combined_grapher ¶
Wrap a ZK grapher so _memory and federated names also route.
_memory-> the read-only memory-source adapter.- a namespaced
<repo_id>:<graph>-> a read-only :class:ZettelGraphpointed at the peer repo's.zettelkasten/(structural load so its embedding index stays lazy and rebuilds locally on first.embeddingsaccess). Cached in the PROCESS-GLOBAL :data:_FED_GRAPH_CACHE(mtime-gated), not per-grapher, so the index stays warm across queries in a long-lived worker. The peer'sgraphs:allowlist is enforced fail-closed. - every other name -> the caller's local grapher, unchanged.
Resolved graphs are memoized for the LIFE OF THIS GRAPHER (one grapher is
built per query). A single query resolves _memory and each box hundreds of
times — via pruning, the keyword/semantic channels, and the reranker's
per-candidate vector fetch — and each unmemoized _memory hit recomputed
storage.source_fileset_signature() (a full glob+stat of every .memory/
file), the dominant warm-query cost at scale. Memoizing per-grapher collapses
that to one resolution per distinct source per query while preserving
freshness: the next query builds a new grapher (and the underlying
:func:get_memory_source / :data:_FED_GRAPH_CACHE still revalidate on their
own), so a within-query snapshot never masks a between-query change.
Source code in zettelkasten/synapse/retrieval.py
resolve_scope ¶
resolve_scope(zk_get_graph: GetGraph, projects: list[str] | None = None, graphs_dir: 'Path | None' = None, include_memory: bool = True) -> tuple[list[str], GetGraph]
Resolve (search_sources, get_graph) for the cross-store scope.
projects=None uses the intersection config; an explicit list overrides
it ([] means "no ZK, memory only"), where each entry is a local project
name or a federated <repo_id>:<project> token. ZK sources are the union
of each scope's :func:~zettelkasten.framing.frame_search_sources,
de-duplicated with order preserved; federated sources are surfaced under
<repo_id>:<graph> names (honoring the peer's allowlist). The _memory
source is appended unless include_memory is False.
Source code in zettelkasten/synapse/retrieval.py
prune_sources ¶
prune_sources(text: str, sources: list[str], get_graph: GetGraph, cap: int | None, *, always_keep: 'tuple[str, ...]' = (MEMORY_SOURCE,)) -> list[str]
Prune sources to the cap most keyword-relevant for text.
Cross-store reads otherwise build one embedding index per in-scope source on
every cold query — the dominant cost and the kglite crash surface. This bounds
the fan-out with a cheap, embedding-free keyword prescan: sources are ranked
by :func:_keyword_relevance and only the top cap survive (ties and
signal-less queries fall back to the original scope order, so the cap always
bounds the fan-out even when keyword signal is weak/absent). Sources in
always_keep (the memory tree by default — small, private practice worth
keeping) are retained and count toward the cap.
Returns the surviving sources in their ORIGINAL scope order (deterministic;
order only breaks reranker ties downstream). cap None/<= 0, or a
scope already within the cap, returns sources unchanged.
Source code in zettelkasten/synapse/retrieval.py
frame_across_stores ¶
frame_across_stores(question: str, zk_get_graph: GetGraph, projects: list[str] | None = None, graphs_dir: 'Path | None' = None, max_sources: int | None = None, **frame_kwargs: Any) -> dict[str, Any]
Frame a question across memory + intersecting ZK, tagging hits by store/tier.
Returns the same shape as :func:zettelkasten.framing.frame_question, with
each finding/claim/other entry annotated with store and tier and a
top-level scope block describing what was searched.
max_sources caps how many in-scope sources are framed against (default:
the synapse config, :func:zettelkasten.synapse.config.max_sources). The
scope is pruned to the most keyword-relevant boxes for question BEFORE
framing so the expensive per-box embedding index build is bounded — the
dominant cold-query cost and the kglite crash surface. 0/negative
disables pruning.
Source code in zettelkasten/synapse/retrieval.py
search_across_stores ¶
search_across_stores(query: str, zk_get_graph: GetGraph, projects: list[str] | None = None, top_k: int = 15, graphs_dir: 'Path | None' = None, expand: bool = False, expand_min_conf: float = 0.6, max_sources: int | None = None) -> dict[str, Any]
Hybrid search across memory + intersecting ZK, fused via RRF.
Delegates to :func:zettelkasten.framing.project_query (per-source keyword +
semantic channels fused with rrf_order, then merged across sources) and
resolves each hit to a result dict tagged by store/tier.
When expand is set, each hit is expanded via the synapse link overlay:
its high-confidence 1-hop cross-store neighbours are appended (tagged
expanded_from + relation) — so a canon hit pulls in the practice that
applies it, and vice versa.
max_sources caps how many in-scope sources are searched (default: the
synapse config, :func:zettelkasten.synapse.config.max_sources). The scope is
pruned to the most keyword-relevant boxes for query BEFORE the hybrid pass
so the expensive per-box embedding index build is bounded — the dominant
cold-query cost and the kglite crash surface. 0/negative disables pruning.
Source code in zettelkasten/synapse/retrieval.py
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 | |