@contextableai/openclaw-memory-rebac 0.1.1 → 0.1.3
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.
package/dist/cli.js
CHANGED
|
@@ -421,17 +421,26 @@ export function registerCommands(cmd, ctx) {
|
|
|
421
421
|
console.log(); // newline after dots
|
|
422
422
|
}
|
|
423
423
|
console.log(`\nDiscovered ${totalFacts} fact(s) across ${totalEpisodes} episode(s).`);
|
|
424
|
+
// Deduplicate: the same edge can appear in multiple episodes
|
|
425
|
+
const seen = new Set();
|
|
426
|
+
const unique = tuples.filter((t) => {
|
|
427
|
+
const key = `${t.resourceType}:${t.resourceId}#${t.relation}@${t.subjectType}:${t.subjectId}`;
|
|
428
|
+
if (seen.has(key))
|
|
429
|
+
return false;
|
|
430
|
+
seen.add(key);
|
|
431
|
+
return true;
|
|
432
|
+
});
|
|
424
433
|
if (opts.dryRun) {
|
|
425
|
-
console.log(`[dry-run] Would write ${
|
|
434
|
+
console.log(`[dry-run] Would write ${unique.length} SpiceDB relationships (${tuples.length - unique.length} duplicates removed).`);
|
|
426
435
|
return;
|
|
427
436
|
}
|
|
428
|
-
if (
|
|
437
|
+
if (unique.length === 0) {
|
|
429
438
|
console.log("No relationships to write.");
|
|
430
439
|
return;
|
|
431
440
|
}
|
|
432
|
-
console.log(`Writing ${
|
|
441
|
+
console.log(`Writing ${unique.length} SpiceDB relationships (${tuples.length - unique.length} duplicates removed)...`);
|
|
433
442
|
try {
|
|
434
|
-
const count = await spicedb.bulkImportRelationships(
|
|
443
|
+
const count = await spicedb.bulkImportRelationships(unique);
|
|
435
444
|
console.log(`Done: ${count} relationships written.`);
|
|
436
445
|
}
|
|
437
446
|
catch (err) {
|
package/dist/spicedb.js
CHANGED
|
@@ -114,11 +114,22 @@ export class SpiceDbClient {
|
|
|
114
114
|
async bulkImportRelationships(tuples, batchSize = 1000) {
|
|
115
115
|
if (tuples.length === 0)
|
|
116
116
|
return 0;
|
|
117
|
-
// Try streaming bulk import first
|
|
117
|
+
// Try streaming bulk import first (uses CREATE semantics — rejects duplicates)
|
|
118
118
|
if (typeof this.promises.bulkImportRelationships === "function") {
|
|
119
|
-
|
|
119
|
+
try {
|
|
120
|
+
return await this.bulkImportViaStream(tuples, batchSize);
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
// ALREADY_EXISTS means some relationships exist (e.g. partial previous run).
|
|
124
|
+
// Fall through to batched writeRelationships which uses TOUCH (idempotent).
|
|
125
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
126
|
+
if (msg.includes("ALREADY_EXISTS")) {
|
|
127
|
+
return this.bulkImportViaWrite(tuples, batchSize);
|
|
128
|
+
}
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
120
131
|
}
|
|
121
|
-
// Fallback: batched writeRelationships
|
|
132
|
+
// Fallback: batched writeRelationships (uses TOUCH — idempotent)
|
|
122
133
|
return this.bulkImportViaWrite(tuples, batchSize);
|
|
123
134
|
}
|
|
124
135
|
bulkImportViaStream(tuples, batchSize) {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# ============================================================================
|
|
2
2
|
# Graphiti FastAPI REST server configuration
|
|
3
3
|
# ============================================================================
|
|
4
|
+
# Copy this file to .env and fill in your values:
|
|
5
|
+
# cp .env.example .env
|
|
6
|
+
#
|
|
4
7
|
# Each AI component (LLM, embedder, reranker) can independently point to a
|
|
5
8
|
# different service. If EMBEDDING_BASE_URL is not set, it defaults to
|
|
6
9
|
# LLM_BASE_URL (or the OpenAI API if LLM_BASE_URL is also unset).
|
|
@@ -12,19 +15,19 @@
|
|
|
12
15
|
# Ollama: http://host.docker.internal:11434/v1
|
|
13
16
|
# vLLM: http://your-vllm-server:8000/v1
|
|
14
17
|
# OpenAI: (leave LLM_BASE_URL empty to use default)
|
|
15
|
-
LLM_BASE_URL=http://
|
|
16
|
-
LLM_MODEL=
|
|
17
|
-
LLM_API_KEY=
|
|
18
|
+
# LLM_BASE_URL=http://host.docker.internal:11434/v1
|
|
19
|
+
LLM_MODEL=gpt-4o-mini
|
|
20
|
+
LLM_API_KEY=your-api-key-here
|
|
18
21
|
|
|
19
22
|
# ============================================================================
|
|
20
23
|
# Embedder
|
|
21
24
|
# ============================================================================
|
|
22
25
|
# Default: OpenAI text-embedding-3-small.
|
|
23
26
|
# If EMBEDDING_BASE_URL is empty, uses LLM_BASE_URL (or OpenAI default).
|
|
24
|
-
EMBEDDING_MODEL=
|
|
27
|
+
# EMBEDDING_MODEL=text-embedding-3-small
|
|
25
28
|
# EMBEDDING_BASE_URL=
|
|
26
29
|
# EMBEDDING_API_KEY=
|
|
27
|
-
EMBEDDING_DIM=
|
|
30
|
+
# EMBEDDING_DIM=
|
|
28
31
|
|
|
29
32
|
# ============================================================================
|
|
30
33
|
# Reranker / cross-encoder
|
|
@@ -98,8 +98,11 @@ def patch():
|
|
|
98
98
|
"WHERE $episode_uuid IN r.episodes "
|
|
99
99
|
"RETURN DISTINCT r.uuid AS uuid"
|
|
100
100
|
)
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
# Use the raw Neo4j async driver directly to avoid differences
|
|
102
|
+
# in the Graphiti Neo4jDriver.execute_query() wrapper across versions.
|
|
103
|
+
raw_driver = singleton_client.driver.client
|
|
104
|
+
records, _, _ = await raw_driver.execute_query(
|
|
105
|
+
query, parameters_={"episode_uuid": episode_uuid}
|
|
103
106
|
)
|
|
104
107
|
return [{"uuid": r["uuid"]} for r in records]
|
|
105
108
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# SpiceDB configuration
|
|
2
|
+
# Copy this file to .env and fill in your values:
|
|
3
|
+
# cp .env.example .env
|
|
2
4
|
|
|
3
|
-
# Preshared key for gRPC authentication
|
|
4
|
-
SPICEDB_PRESHARED_KEY=
|
|
5
|
+
# Preshared key for gRPC authentication (CHANGE THIS for production!)
|
|
6
|
+
SPICEDB_PRESHARED_KEY=your-preshared-key-here
|
|
5
7
|
|
|
6
8
|
# PostgreSQL password for SpiceDB backing store
|
|
7
9
|
# SPICEDB_POSTGRES_PASSWORD=spicedb
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contextableai/openclaw-memory-rebac",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "OpenClaw two-layer memory plugin: SpiceDB ReBAC authorization + Graphiti knowledge graph",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,10 @@
|
|
|
23
23
|
"openclaw.plugin.json",
|
|
24
24
|
"plugin.defaults.json",
|
|
25
25
|
"schema.zed",
|
|
26
|
-
"docker
|
|
26
|
+
"docker/**/*.yml",
|
|
27
|
+
"docker/**/*.py",
|
|
28
|
+
"docker/**/*.example",
|
|
29
|
+
"docker/**/Dockerfile",
|
|
27
30
|
"scripts/",
|
|
28
31
|
"bin/"
|
|
29
32
|
],
|