@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.
Files changed (71) hide show
  1. package/.env.example +4 -2
  2. package/config-shared.js +1 -0
  3. package/package.json +4 -1
  4. package/sinain-agent/run.sh +36 -4
  5. package/sinain-core/package-lock.json +963 -0
  6. package/sinain-core/package.json +1 -0
  7. package/sinain-core/src/buffers/feed-buffer.ts +34 -0
  8. package/sinain-core/src/embedding/service.ts +66 -0
  9. package/sinain-core/src/index.ts +65 -17
  10. package/sinain-core/src/learning/local-curation.ts +137 -7
  11. package/sinain-core/src/server.ts +31 -0
  12. package/sinain-memory/README.md +105 -0
  13. package/sinain-memory/embed_client.py +117 -0
  14. package/sinain-memory/graph_query.py +269 -18
  15. package/sinain-memory/knowledge_integrator.py +551 -74
  16. package/sinain-memory/memory-config.json +1 -1
  17. package/sinain-memory/session_distiller.py +43 -19
  18. package/sinain-memory/triplestore.py +60 -0
  19. package/sinain-memory/__pycache__/common.cpython-312.pyc +0 -0
  20. package/sinain-memory/__pycache__/graph_query.cpython-312.pyc +0 -0
  21. package/sinain-memory/__pycache__/knowledge_integrator.cpython-312.pyc +0 -0
  22. package/sinain-memory/__pycache__/session_distiller.cpython-312.pyc +0 -0
  23. package/sinain-memory/__pycache__/triplestore.cpython-312.pyc +0 -0
  24. package/sinain-memory/eval/__init__.py +0 -0
  25. package/sinain-memory/eval/__pycache__/__init__.cpython-312.pyc +0 -0
  26. package/sinain-memory/eval/assertions.py +0 -267
  27. package/sinain-memory/eval/benchmarks/__init__.py +0 -0
  28. package/sinain-memory/eval/benchmarks/__pycache__/__init__.cpython-312.pyc +0 -0
  29. package/sinain-memory/eval/benchmarks/__pycache__/base_adapter.cpython-312.pyc +0 -0
  30. package/sinain-memory/eval/benchmarks/__pycache__/config.cpython-312.pyc +0 -0
  31. package/sinain-memory/eval/benchmarks/__pycache__/evaluate.cpython-312.pyc +0 -0
  32. package/sinain-memory/eval/benchmarks/__pycache__/ingest.cpython-312.pyc +0 -0
  33. package/sinain-memory/eval/benchmarks/__pycache__/longmemeval_adapter.cpython-312.pyc +0 -0
  34. package/sinain-memory/eval/benchmarks/__pycache__/query.cpython-312.pyc +0 -0
  35. package/sinain-memory/eval/benchmarks/__pycache__/report.cpython-312.pyc +0 -0
  36. package/sinain-memory/eval/benchmarks/__pycache__/runner.cpython-312.pyc +0 -0
  37. package/sinain-memory/eval/benchmarks/base_adapter.py +0 -43
  38. package/sinain-memory/eval/benchmarks/config.py +0 -23
  39. package/sinain-memory/eval/benchmarks/evaluate.py +0 -146
  40. package/sinain-memory/eval/benchmarks/ingest.py +0 -152
  41. package/sinain-memory/eval/benchmarks/judges/__init__.py +0 -0
  42. package/sinain-memory/eval/benchmarks/judges/__pycache__/__init__.cpython-312.pyc +0 -0
  43. package/sinain-memory/eval/benchmarks/judges/__pycache__/qa_judge.cpython-312.pyc +0 -0
  44. package/sinain-memory/eval/benchmarks/judges/qa_judge.py +0 -81
  45. package/sinain-memory/eval/benchmarks/longmemeval_adapter.py +0 -177
  46. package/sinain-memory/eval/benchmarks/query.py +0 -172
  47. package/sinain-memory/eval/benchmarks/report.py +0 -87
  48. package/sinain-memory/eval/benchmarks/runner.py +0 -276
  49. package/sinain-memory/eval/judges/__init__.py +0 -0
  50. package/sinain-memory/eval/judges/base_judge.py +0 -61
  51. package/sinain-memory/eval/judges/curation_judge.py +0 -46
  52. package/sinain-memory/eval/judges/insight_judge.py +0 -48
  53. package/sinain-memory/eval/judges/mining_judge.py +0 -42
  54. package/sinain-memory/eval/judges/signal_judge.py +0 -45
  55. package/sinain-memory/eval/retrieval_benchmark.jsonl +0 -12
  56. package/sinain-memory/eval/retrieval_evaluator.py +0 -186
  57. package/sinain-memory/eval/schemas.py +0 -247
  58. package/sinain-memory/tests/__init__.py +0 -0
  59. package/sinain-memory/tests/conftest.py +0 -189
  60. package/sinain-memory/tests/test_curator_helpers.py +0 -94
  61. package/sinain-memory/tests/test_embedder.py +0 -210
  62. package/sinain-memory/tests/test_extract_json.py +0 -124
  63. package/sinain-memory/tests/test_feedback_computation.py +0 -121
  64. package/sinain-memory/tests/test_miner_helpers.py +0 -71
  65. package/sinain-memory/tests/test_module_management.py +0 -458
  66. package/sinain-memory/tests/test_parsers.py +0 -96
  67. package/sinain-memory/tests/test_tick_evaluator.py +0 -430
  68. package/sinain-memory/tests/test_triple_extractor.py +0 -255
  69. package/sinain-memory/tests/test_triple_ingest.py +0 -191
  70. package/sinain-memory/tests/test_triple_migrate.py +0 -138
  71. 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"