@geravant/sinain 1.12.0 → 1.14.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.
- package/.env.example +4 -2
- package/config-shared.js +1 -0
- package/package.json +4 -1
- package/sinain-agent/run.sh +36 -4
- package/sinain-core/package-lock.json +963 -0
- package/sinain-core/package.json +1 -0
- package/sinain-core/src/buffers/feed-buffer.ts +34 -0
- package/sinain-core/src/embedding/service.ts +66 -0
- package/sinain-core/src/index.ts +65 -17
- package/sinain-core/src/learning/local-curation.ts +137 -7
- package/sinain-core/src/server.ts +31 -0
- package/sinain-memory/README.md +105 -0
- package/sinain-memory/embed_client.py +117 -0
- package/sinain-memory/graph_query.py +269 -18
- package/sinain-memory/knowledge_integrator.py +551 -74
- package/sinain-memory/memory-config.json +1 -1
- package/sinain-memory/session_distiller.py +43 -19
- package/sinain-memory/triplestore.py +60 -0
- package/sinain-memory/__pycache__/common.cpython-312.pyc +0 -0
- package/sinain-memory/__pycache__/graph_query.cpython-312.pyc +0 -0
- package/sinain-memory/__pycache__/knowledge_integrator.cpython-312.pyc +0 -0
- package/sinain-memory/__pycache__/session_distiller.cpython-312.pyc +0 -0
- package/sinain-memory/__pycache__/triplestore.cpython-312.pyc +0 -0
- package/sinain-memory/eval/__init__.py +0 -0
- package/sinain-memory/eval/__pycache__/__init__.cpython-312.pyc +0 -0
- package/sinain-memory/eval/assertions.py +0 -267
- package/sinain-memory/eval/benchmarks/__init__.py +0 -0
- package/sinain-memory/eval/benchmarks/__pycache__/__init__.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/__pycache__/base_adapter.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/__pycache__/config.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/__pycache__/evaluate.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/__pycache__/ingest.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/__pycache__/longmemeval_adapter.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/__pycache__/query.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/__pycache__/report.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/__pycache__/runner.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/base_adapter.py +0 -43
- package/sinain-memory/eval/benchmarks/config.py +0 -23
- package/sinain-memory/eval/benchmarks/evaluate.py +0 -146
- package/sinain-memory/eval/benchmarks/ingest.py +0 -152
- package/sinain-memory/eval/benchmarks/judges/__init__.py +0 -0
- package/sinain-memory/eval/benchmarks/judges/__pycache__/__init__.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/judges/__pycache__/qa_judge.cpython-312.pyc +0 -0
- package/sinain-memory/eval/benchmarks/judges/qa_judge.py +0 -81
- package/sinain-memory/eval/benchmarks/longmemeval_adapter.py +0 -177
- package/sinain-memory/eval/benchmarks/query.py +0 -172
- package/sinain-memory/eval/benchmarks/report.py +0 -87
- package/sinain-memory/eval/benchmarks/runner.py +0 -276
- package/sinain-memory/eval/judges/__init__.py +0 -0
- package/sinain-memory/eval/judges/base_judge.py +0 -61
- package/sinain-memory/eval/judges/curation_judge.py +0 -46
- package/sinain-memory/eval/judges/insight_judge.py +0 -48
- package/sinain-memory/eval/judges/mining_judge.py +0 -42
- package/sinain-memory/eval/judges/signal_judge.py +0 -45
- package/sinain-memory/eval/retrieval_benchmark.jsonl +0 -12
- package/sinain-memory/eval/retrieval_evaluator.py +0 -186
- package/sinain-memory/eval/schemas.py +0 -247
- package/sinain-memory/tests/__init__.py +0 -0
- package/sinain-memory/tests/conftest.py +0 -189
- package/sinain-memory/tests/test_curator_helpers.py +0 -94
- package/sinain-memory/tests/test_embedder.py +0 -210
- package/sinain-memory/tests/test_extract_json.py +0 -124
- package/sinain-memory/tests/test_feedback_computation.py +0 -121
- package/sinain-memory/tests/test_miner_helpers.py +0 -71
- package/sinain-memory/tests/test_module_management.py +0 -458
- package/sinain-memory/tests/test_parsers.py +0 -96
- package/sinain-memory/tests/test_tick_evaluator.py +0 -430
- package/sinain-memory/tests/test_triple_extractor.py +0 -255
- package/sinain-memory/tests/test_triple_ingest.py +0 -191
- package/sinain-memory/tests/test_triple_migrate.py +0 -138
- package/sinain-memory/tests/test_triplestore.py +0 -248
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
"""Tests for triplestore.py — EAV triple store."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import pytest
|
|
5
|
-
from triplestore import TripleStore
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@pytest.fixture
|
|
9
|
-
def store(tmp_path):
|
|
10
|
-
"""Create a fresh triple store in a temp directory."""
|
|
11
|
-
db_path = tmp_path / "test.db"
|
|
12
|
-
s = TripleStore(str(db_path))
|
|
13
|
-
yield s
|
|
14
|
-
s.close()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@pytest.fixture
|
|
18
|
-
def populated_store(store):
|
|
19
|
-
"""Store with sample data across two transactions."""
|
|
20
|
-
tx1 = store.begin_tx("test", session_key="sess-1")
|
|
21
|
-
store.assert_triple(tx1, "signal:2026-03-01T10:00", "description", "OCR stall")
|
|
22
|
-
store.assert_triple(tx1, "signal:2026-03-01T10:00", "priority", "high")
|
|
23
|
-
store.assert_triple(tx1, "signal:2026-03-01T10:00", "related_to", "concept:ocr", "ref")
|
|
24
|
-
store.assert_triple(tx1, "concept:ocr", "name", "OCR")
|
|
25
|
-
store.assert_triple(tx1, "pattern:frame-batch", "text", "Frame batching improves OCR")
|
|
26
|
-
store.assert_triple(tx1, "pattern:frame-batch", "related_to", "concept:ocr", "ref")
|
|
27
|
-
store.assert_triple(tx1, "session:2026-03-01T09:00", "summary", "Debugging OCR pipeline")
|
|
28
|
-
store.assert_triple(tx1, "session:2026-03-01T09:00", "related_to", "concept:ocr", "ref")
|
|
29
|
-
store._tx1 = tx1 # stash for tests
|
|
30
|
-
return store
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class TestTransactions:
|
|
34
|
-
def test_begin_tx_returns_positive_id(self, store):
|
|
35
|
-
tx = store.begin_tx("test")
|
|
36
|
-
assert tx > 0
|
|
37
|
-
|
|
38
|
-
def test_latest_tx_empty_store(self, tmp_path):
|
|
39
|
-
s = TripleStore(str(tmp_path / "empty.db"))
|
|
40
|
-
assert s.latest_tx() == 0
|
|
41
|
-
s.close()
|
|
42
|
-
|
|
43
|
-
def test_latest_tx_after_writes(self, store):
|
|
44
|
-
tx1 = store.begin_tx("a")
|
|
45
|
-
tx2 = store.begin_tx("b")
|
|
46
|
-
assert store.latest_tx() == tx2
|
|
47
|
-
assert tx2 > tx1
|
|
48
|
-
|
|
49
|
-
def test_tx_metadata(self, store):
|
|
50
|
-
tx = store.begin_tx("test", metadata={"foo": "bar"})
|
|
51
|
-
row = store._conn.execute(
|
|
52
|
-
"SELECT metadata FROM transactions WHERE tx_id = ?", (tx,)
|
|
53
|
-
).fetchone()
|
|
54
|
-
import json
|
|
55
|
-
assert json.loads(row["metadata"]) == {"foo": "bar"}
|
|
56
|
-
|
|
57
|
-
def test_tx_parent(self, store):
|
|
58
|
-
tx1 = store.begin_tx("parent")
|
|
59
|
-
tx2 = store.begin_tx("child", parent_tx=tx1)
|
|
60
|
-
row = store._conn.execute(
|
|
61
|
-
"SELECT parent_tx FROM transactions WHERE tx_id = ?", (tx2,)
|
|
62
|
-
).fetchone()
|
|
63
|
-
assert row["parent_tx"] == tx1
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class TestAssertAndEntity:
|
|
67
|
-
def test_assert_returns_id(self, store):
|
|
68
|
-
tx = store.begin_tx("test")
|
|
69
|
-
tid = store.assert_triple(tx, "e:1", "name", "Test")
|
|
70
|
-
assert tid > 0
|
|
71
|
-
|
|
72
|
-
def test_entity_returns_all_attrs(self, populated_store):
|
|
73
|
-
ent = populated_store.entity("signal:2026-03-01T10:00")
|
|
74
|
-
assert ent["description"] == ["OCR stall"]
|
|
75
|
-
assert ent["priority"] == ["high"]
|
|
76
|
-
assert ent["related_to"] == ["concept:ocr"]
|
|
77
|
-
|
|
78
|
-
def test_entity_missing_returns_empty(self, store):
|
|
79
|
-
assert store.entity("nonexistent:1") == {}
|
|
80
|
-
|
|
81
|
-
def test_multiple_values_per_attr(self, store):
|
|
82
|
-
tx = store.begin_tx("test")
|
|
83
|
-
store.assert_triple(tx, "e:1", "tag", "alpha")
|
|
84
|
-
store.assert_triple(tx, "e:1", "tag", "beta")
|
|
85
|
-
ent = store.entity("e:1")
|
|
86
|
-
assert set(ent["tag"]) == {"alpha", "beta"}
|
|
87
|
-
|
|
88
|
-
def test_entity_type_auto_populated(self, store):
|
|
89
|
-
tx = store.begin_tx("test")
|
|
90
|
-
store.assert_triple(tx, "signal:abc", "x", "y")
|
|
91
|
-
row = store._conn.execute(
|
|
92
|
-
"SELECT entity_type FROM entity_types WHERE entity_id = ?",
|
|
93
|
-
("signal:abc",),
|
|
94
|
-
).fetchone()
|
|
95
|
-
assert row["entity_type"] == "signal"
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
class TestRetraction:
|
|
99
|
-
def test_retract_by_attr(self, populated_store):
|
|
100
|
-
tx2 = populated_store.begin_tx("retract")
|
|
101
|
-
count = populated_store.retract_triple(tx2, "signal:2026-03-01T10:00", "priority")
|
|
102
|
-
assert count == 1
|
|
103
|
-
ent = populated_store.entity("signal:2026-03-01T10:00")
|
|
104
|
-
assert "priority" not in ent
|
|
105
|
-
|
|
106
|
-
def test_retract_by_attr_and_value(self, store):
|
|
107
|
-
tx = store.begin_tx("test")
|
|
108
|
-
store.assert_triple(tx, "e:1", "tag", "a")
|
|
109
|
-
store.assert_triple(tx, "e:1", "tag", "b")
|
|
110
|
-
tx2 = store.begin_tx("retract")
|
|
111
|
-
count = store.retract_triple(tx2, "e:1", "tag", "a")
|
|
112
|
-
assert count == 1
|
|
113
|
-
ent = store.entity("e:1")
|
|
114
|
-
assert ent["tag"] == ["b"]
|
|
115
|
-
|
|
116
|
-
def test_retract_nonexistent_returns_zero(self, store):
|
|
117
|
-
tx = store.begin_tx("test")
|
|
118
|
-
count = store.retract_triple(tx, "e:nope", "attr")
|
|
119
|
-
assert count == 0
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
class TestEAVTAsOfTx:
|
|
123
|
-
def test_as_of_tx_sees_old_state(self, populated_store):
|
|
124
|
-
tx1 = populated_store._tx1
|
|
125
|
-
tx2 = populated_store.begin_tx("change")
|
|
126
|
-
populated_store.retract_triple(tx2, "signal:2026-03-01T10:00", "priority")
|
|
127
|
-
|
|
128
|
-
# Current state: no priority
|
|
129
|
-
assert "priority" not in populated_store.entity("signal:2026-03-01T10:00")
|
|
130
|
-
# as_of tx1: has priority
|
|
131
|
-
assert "priority" in populated_store.entity("signal:2026-03-01T10:00", as_of_tx=tx1)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
class TestAEVT:
|
|
135
|
-
def test_entities_with_attr(self, populated_store):
|
|
136
|
-
results = populated_store.entities_with_attr("name")
|
|
137
|
-
assert ("concept:ocr", "OCR") in results
|
|
138
|
-
|
|
139
|
-
def test_entities_with_attr_multiple(self, populated_store):
|
|
140
|
-
results = populated_store.entities_with_attr("related_to")
|
|
141
|
-
eids = [r[0] for r in results]
|
|
142
|
-
assert "signal:2026-03-01T10:00" in eids
|
|
143
|
-
assert "pattern:frame-batch" in eids
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
class TestVAET:
|
|
147
|
-
def test_backrefs(self, populated_store):
|
|
148
|
-
refs = populated_store.backrefs("concept:ocr")
|
|
149
|
-
eids = [r[0] for r in refs]
|
|
150
|
-
assert "signal:2026-03-01T10:00" in eids
|
|
151
|
-
assert "pattern:frame-batch" in eids
|
|
152
|
-
assert "session:2026-03-01T09:00" in eids
|
|
153
|
-
|
|
154
|
-
def test_backrefs_with_attribute_filter(self, populated_store):
|
|
155
|
-
refs = populated_store.backrefs("concept:ocr", attribute="related_to")
|
|
156
|
-
eids = [r[0] for r in refs]
|
|
157
|
-
assert "signal:2026-03-01T10:00" in eids
|
|
158
|
-
|
|
159
|
-
def test_backrefs_no_results(self, store):
|
|
160
|
-
assert store.backrefs("nonexistent:1") == []
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
class TestAVET:
|
|
164
|
-
def test_lookup(self, populated_store):
|
|
165
|
-
found = populated_store.lookup("name", "OCR")
|
|
166
|
-
assert "concept:ocr" in found
|
|
167
|
-
|
|
168
|
-
def test_lookup_no_match(self, populated_store):
|
|
169
|
-
found = populated_store.lookup("name", "nonexistent")
|
|
170
|
-
assert found == []
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
class TestNeighbors:
|
|
174
|
-
def test_neighbors_depth_1(self, populated_store):
|
|
175
|
-
nbrs = populated_store.neighbors("concept:ocr", depth=1)
|
|
176
|
-
assert "concept:ocr" in nbrs
|
|
177
|
-
# Should find signal, pattern, and session via backrefs
|
|
178
|
-
found_eids = set(nbrs.keys())
|
|
179
|
-
assert "signal:2026-03-01T10:00" in found_eids or "pattern:frame-batch" in found_eids
|
|
180
|
-
|
|
181
|
-
def test_neighbors_depth_0(self, populated_store):
|
|
182
|
-
nbrs = populated_store.neighbors("concept:ocr", depth=0)
|
|
183
|
-
assert "concept:ocr" in nbrs
|
|
184
|
-
assert len(nbrs) == 1 # only the entity itself
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
class TestNovelty:
|
|
188
|
-
def test_novelty_after_tx(self, populated_store):
|
|
189
|
-
tx1 = populated_store._tx1
|
|
190
|
-
tx2 = populated_store.begin_tx("new")
|
|
191
|
-
populated_store.assert_triple(tx2, "concept:new", "name", "New Concept")
|
|
192
|
-
changes = populated_store.novelty(tx1)
|
|
193
|
-
assert len(changes) >= 1
|
|
194
|
-
assert any(c["entity_id"] == "concept:new" for c in changes)
|
|
195
|
-
|
|
196
|
-
def test_novelty_bounded(self, populated_store):
|
|
197
|
-
tx1 = populated_store._tx1
|
|
198
|
-
tx2 = populated_store.begin_tx("a")
|
|
199
|
-
populated_store.assert_triple(tx2, "concept:a", "name", "A")
|
|
200
|
-
tx3 = populated_store.begin_tx("b")
|
|
201
|
-
populated_store.assert_triple(tx3, "concept:b", "name", "B")
|
|
202
|
-
# Only changes between tx1 and tx2
|
|
203
|
-
changes = populated_store.novelty(tx1, until_tx=tx2)
|
|
204
|
-
eids = [c["entity_id"] for c in changes]
|
|
205
|
-
assert "concept:a" in eids
|
|
206
|
-
assert "concept:b" not in eids
|
|
207
|
-
|
|
208
|
-
def test_novelty_empty(self, store):
|
|
209
|
-
tx = store.begin_tx("test")
|
|
210
|
-
assert store.novelty(tx) == []
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
class TestGC:
|
|
214
|
-
def test_gc_deletes_old_retracted(self, store):
|
|
215
|
-
tx = store.begin_tx("test")
|
|
216
|
-
store.assert_triple(tx, "e:1", "x", "y")
|
|
217
|
-
tx2 = store.begin_tx("retract")
|
|
218
|
-
store.retract_triple(tx2, "e:1", "x")
|
|
219
|
-
# GC with 0 days should delete it
|
|
220
|
-
count = store.gc(older_than_days=0)
|
|
221
|
-
assert count >= 1
|
|
222
|
-
|
|
223
|
-
def test_gc_preserves_active(self, store):
|
|
224
|
-
tx = store.begin_tx("test")
|
|
225
|
-
store.assert_triple(tx, "e:1", "x", "y")
|
|
226
|
-
count = store.gc(older_than_days=0)
|
|
227
|
-
assert count == 0 # not retracted, shouldn't be deleted
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
class TestStats:
|
|
231
|
-
def test_stats_populated(self, populated_store):
|
|
232
|
-
s = populated_store.stats()
|
|
233
|
-
assert s["triples"] >= 8
|
|
234
|
-
assert s["entities"] >= 4
|
|
235
|
-
assert s["transactions"] >= 1
|
|
236
|
-
assert s["db_size_bytes"] > 0
|
|
237
|
-
|
|
238
|
-
def test_stats_empty(self, store):
|
|
239
|
-
s = store.stats()
|
|
240
|
-
assert s["triples"] == 0
|
|
241
|
-
assert s["entities"] == 0
|
|
242
|
-
assert s["transactions"] == 0
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
class TestWALMode:
|
|
246
|
-
def test_wal_mode_enabled(self, store):
|
|
247
|
-
mode = store._conn.execute("PRAGMA journal_mode").fetchone()[0]
|
|
248
|
-
assert mode == "wal"
|