@contextableai/openclaw-memory-rebac 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -149,6 +149,25 @@ def patch():
|
|
|
149
149
|
'created_at', 'labels',
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
# Edge names that represent deduplication artifacts, not real semantic facts.
|
|
153
|
+
# Older graphiti-core versions created these during node resolution; the LLM
|
|
154
|
+
# may also extract them as relationships. Neither source is useful — upstream
|
|
155
|
+
# abandoned IS_DUPLICATE_OF edges and the information is redundant with node
|
|
156
|
+
# UUID reuse. Matched case-insensitively with space/underscore normalization.
|
|
157
|
+
# See: https://github.com/contextablemark/openclaw-memory-rebac/issues/12
|
|
158
|
+
RESERVED_EDGE_NAMES = {
|
|
159
|
+
'IS_DUPLICATE_OF',
|
|
160
|
+
'DUPLICATE_OF',
|
|
161
|
+
'HAS_DUPLICATE',
|
|
162
|
+
'DUPLICATES',
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
def _is_reserved_edge_name(name):
|
|
166
|
+
"""Check if an edge name is a deduplication artifact."""
|
|
167
|
+
if not name:
|
|
168
|
+
return False
|
|
169
|
+
return name.strip().upper().replace(" ", "_") in RESERVED_EDGE_NAMES
|
|
170
|
+
|
|
152
171
|
def _sanitize_attributes(attrs, reserved_keys):
|
|
153
172
|
"""Flatten non-primitive values and strip reserved keys to prevent clobber."""
|
|
154
173
|
if not attrs:
|
|
@@ -194,6 +213,31 @@ def patch():
|
|
|
194
213
|
list((edge.attributes or {}).keys()),
|
|
195
214
|
edge.source_node_uuid, edge.target_node_uuid,
|
|
196
215
|
)
|
|
216
|
+
# Filter deduplication-artifact edges (IS_DUPLICATE_OF and variants).
|
|
217
|
+
# These are not real facts — they are structural metadata that upstream
|
|
218
|
+
# graphiti-core has since abandoned.
|
|
219
|
+
original_edge_count = len(entity_edges)
|
|
220
|
+
entity_edges = [e for e in entity_edges if not _is_reserved_edge_name(e.name)]
|
|
221
|
+
dup_filtered = original_edge_count - len(entity_edges)
|
|
222
|
+
if dup_filtered:
|
|
223
|
+
logger.warning(
|
|
224
|
+
"DIAG duplicate_edge_filtered: removed %d IS_DUPLICATE_OF-family "
|
|
225
|
+
"edge(s) at bulk_add level", dup_filtered,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Filter self-referential edges (entity relating to itself).
|
|
229
|
+
pre_self_count = len(entity_edges)
|
|
230
|
+
entity_edges = [
|
|
231
|
+
e for e in entity_edges
|
|
232
|
+
if e.source_node_uuid != e.target_node_uuid
|
|
233
|
+
]
|
|
234
|
+
self_ref_count = pre_self_count - len(entity_edges)
|
|
235
|
+
if self_ref_count:
|
|
236
|
+
logger.warning(
|
|
237
|
+
"DIAG self_ref_edge_filtered: removed %d self-referential edge(s)",
|
|
238
|
+
self_ref_count,
|
|
239
|
+
)
|
|
240
|
+
|
|
197
241
|
return await original_bulk_add(
|
|
198
242
|
driver, episodic_nodes, episodic_edges,
|
|
199
243
|
entity_nodes, entity_edges, embedder,
|
|
@@ -253,15 +297,26 @@ def patch():
|
|
|
253
297
|
except Exception as _patch_err:
|
|
254
298
|
logger.warning("Could not patch ExtractedEdges for None-index filtering: %s", _patch_err)
|
|
255
299
|
|
|
256
|
-
# Fallback: catch any remaining TypeError from the whole function
|
|
300
|
+
# Fallback: catch any remaining TypeError from the whole function,
|
|
301
|
+
# and filter IS_DUPLICATE_OF edges early (before embedding computation).
|
|
257
302
|
async def safe_extract_edges(*args, **kwargs):
|
|
258
303
|
try:
|
|
259
|
-
|
|
304
|
+
result = await original_extract_edges(*args, **kwargs)
|
|
260
305
|
except TypeError as e:
|
|
261
306
|
if "not supported between instances" in str(e):
|
|
262
307
|
logger.warning("extract_edges skipped due to LLM output issue: %s", e)
|
|
263
308
|
return []
|
|
264
309
|
raise
|
|
310
|
+
if result:
|
|
311
|
+
original_len = len(result)
|
|
312
|
+
result = [e for e in result if not _is_reserved_edge_name(e.name)]
|
|
313
|
+
dropped = original_len - len(result)
|
|
314
|
+
if dropped:
|
|
315
|
+
logger.warning(
|
|
316
|
+
"DIAG duplicate_edge_filtered: removed %d IS_DUPLICATE_OF-family "
|
|
317
|
+
"edge(s) at extract_edges level", dropped,
|
|
318
|
+
)
|
|
319
|
+
return result
|
|
265
320
|
|
|
266
321
|
edge_ops_mod.extract_edges = safe_extract_edges
|
|
267
322
|
# Patch the local binding in graphiti.py
|
|
@@ -56,8 +56,8 @@ services:
|
|
|
56
56
|
command: serve
|
|
57
57
|
restart: unless-stopped
|
|
58
58
|
ports:
|
|
59
|
-
- "
|
|
60
|
-
- "
|
|
59
|
+
- "127.0.0.1:${SPICEDB_GRPC_PORT:-50051}:50051" # gRPC
|
|
60
|
+
- "127.0.0.1:${SPICEDB_HTTP_PORT:-8080}:8080" # HTTP metrics / healthz
|
|
61
61
|
environment:
|
|
62
62
|
SPICEDB_GRPC_PRESHARED_KEY: ${SPICEDB_PRESHARED_KEY:-dev_token}
|
|
63
63
|
SPICEDB_DATASTORE_ENGINE: postgres
|
package/package.json
CHANGED