symfonic.memory.janitor¶
janitor ¶
SemanticMerge -- deduplicates procedural memory nodes by semantic similarity.
Uses difflib.SequenceMatcher for label/content comparison so no additional dependencies are required. The default threshold of 0.65 captures cross-language similarities (e.g. "Ventas" vs "Sales") while still avoiding false positives on unrelated content.
SemanticMerge ¶
Bases: SemanticDeduplicationMixin
Deduplicates procedural nodes by semantic (textual) similarity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threshold
|
float
|
Minimum similarity score (0.0–1.0) required for two nodes to be considered duplicates. Defaults to 0.75. |
0.65
|
Source code in src/symfonic/memory/janitor.py
find_duplicates
async
¶
find_duplicates(scope: TenantScope, existing_nodes: list[MemoryNode], new_node: MemoryNode) -> list[MemoryNode]
Return nodes from existing_nodes that overlap with new_node.
A node is considered a duplicate when the similarity between its
representative text and that of new_node meets or exceeds
self.threshold. Nodes whose representative text is shorter than
_MIN_TEXT_LENGTH characters are skipped to avoid false positives
caused by incidental substring overlap in short identifiers.
v7.5 authored-tier guard (adopter Ask 7 Option B):
* When new_node itself is authored, return [] -- the
caller's store_skill call must produce a fresh graph
row even if a near-identical auto-promoted entry already
exists. Domain plugins own the authored skill's
lifecycle.
* Authored entries in existing_nodes are skipped as merge
candidates so an incoming auto-promoted draft cannot
absorb the authored canonical (which would mutate the
hand-edited fields).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scope
|
TenantScope
|
Tenant context (used for logging only at this level). |
required |
existing_nodes
|
list[MemoryNode]
|
Candidate nodes already stored in the graph. |
required |
new_node
|
MemoryNode
|
The incoming node to compare against. |
required |
Returns:
| Type | Description |
|---|---|
list[MemoryNode]
|
Subset of existing_nodes whose similarity score >= threshold. |
Source code in src/symfonic/memory/janitor.py
merge
async
¶
Merge new into existing and return the canonical node.
Merge rules: - Reviewed canonical: An automatic Phase 12 draft cannot modify an existing approved or rejected procedure. - Label: Keep the longer label (assumed more specific / descriptive). - Steps: Union of both step lists, preserving order and removing exact duplicates. - Importance: Take the maximum of the two scores. - Active flag: True if either node was active. - Properties: Shallow-merge; existing values are overridden by new values, except for the fields governed by the rules above. - Identity: The existing node's ID is preserved so callers can update the graph record in-place rather than creating a new one.