@simbimbo/memory-ocmemog 0.1.11 → 0.1.13
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/CHANGELOG.md +30 -0
- package/README.md +83 -18
- package/brain/runtime/__init__.py +2 -12
- package/brain/runtime/config.py +1 -24
- package/brain/runtime/inference.py +1 -151
- package/brain/runtime/instrumentation.py +1 -15
- package/brain/runtime/memory/__init__.py +3 -13
- package/brain/runtime/memory/api.py +1 -1219
- package/brain/runtime/memory/candidate.py +1 -185
- package/brain/runtime/memory/conversation_state.py +1 -1823
- package/brain/runtime/memory/distill.py +1 -344
- package/brain/runtime/memory/embedding_engine.py +1 -92
- package/brain/runtime/memory/freshness.py +1 -112
- package/brain/runtime/memory/health.py +1 -40
- package/brain/runtime/memory/integrity.py +1 -186
- package/brain/runtime/memory/memory_consolidation.py +1 -58
- package/brain/runtime/memory/memory_links.py +1 -107
- package/brain/runtime/memory/memory_salience.py +1 -233
- package/brain/runtime/memory/memory_synthesis.py +1 -31
- package/brain/runtime/memory/memory_taxonomy.py +1 -33
- package/brain/runtime/memory/pondering_engine.py +1 -654
- package/brain/runtime/memory/promote.py +1 -277
- package/brain/runtime/memory/provenance.py +1 -406
- package/brain/runtime/memory/reinforcement.py +1 -71
- package/brain/runtime/memory/retrieval.py +1 -210
- package/brain/runtime/memory/semantic_search.py +1 -64
- package/brain/runtime/memory/store.py +1 -429
- package/brain/runtime/memory/unresolved_state.py +1 -91
- package/brain/runtime/memory/vector_index.py +1 -323
- package/brain/runtime/model_roles.py +1 -9
- package/brain/runtime/model_router.py +1 -22
- package/brain/runtime/providers.py +1 -66
- package/brain/runtime/security/redaction.py +1 -12
- package/brain/runtime/state_store.py +1 -23
- package/brain/runtime/storage_paths.py +1 -39
- package/docs/architecture/memory.md +20 -24
- package/docs/release-checklist.md +19 -6
- package/docs/usage.md +33 -17
- package/index.ts +8 -1
- package/ocmemog/__init__.py +11 -0
- package/ocmemog/doctor.py +1255 -0
- package/ocmemog/runtime/__init__.py +18 -0
- package/ocmemog/runtime/_compat_bridge.py +28 -0
- package/ocmemog/runtime/config.py +34 -0
- package/ocmemog/runtime/identity.py +115 -0
- package/ocmemog/runtime/inference.py +163 -0
- package/ocmemog/runtime/instrumentation.py +20 -0
- package/ocmemog/runtime/memory/__init__.py +91 -0
- package/ocmemog/runtime/memory/api.py +1594 -0
- package/ocmemog/runtime/memory/candidate.py +192 -0
- package/ocmemog/runtime/memory/conversation_state.py +1831 -0
- package/ocmemog/runtime/memory/distill.py +282 -0
- package/ocmemog/runtime/memory/embedding_engine.py +151 -0
- package/ocmemog/runtime/memory/freshness.py +114 -0
- package/ocmemog/runtime/memory/health.py +93 -0
- package/ocmemog/runtime/memory/integrity.py +208 -0
- package/ocmemog/runtime/memory/memory_consolidation.py +60 -0
- package/ocmemog/runtime/memory/memory_links.py +109 -0
- package/ocmemog/runtime/memory/memory_salience.py +235 -0
- package/ocmemog/runtime/memory/memory_synthesis.py +33 -0
- package/ocmemog/runtime/memory/memory_taxonomy.py +35 -0
- package/ocmemog/runtime/memory/pondering_engine.py +681 -0
- package/ocmemog/runtime/memory/promote.py +279 -0
- package/ocmemog/runtime/memory/provenance.py +408 -0
- package/ocmemog/runtime/memory/reinforcement.py +73 -0
- package/ocmemog/runtime/memory/retrieval.py +224 -0
- package/ocmemog/runtime/memory/semantic_search.py +66 -0
- package/ocmemog/runtime/memory/store.py +433 -0
- package/ocmemog/runtime/memory/unresolved_state.py +93 -0
- package/ocmemog/runtime/memory/vector_index.py +411 -0
- package/ocmemog/runtime/model_roles.py +15 -0
- package/ocmemog/runtime/model_router.py +28 -0
- package/ocmemog/runtime/providers.py +78 -0
- package/ocmemog/runtime/roles.py +92 -0
- package/ocmemog/runtime/security/__init__.py +8 -0
- package/ocmemog/runtime/security/redaction.py +17 -0
- package/ocmemog/runtime/state_store.py +32 -0
- package/ocmemog/runtime/storage_paths.py +70 -0
- package/ocmemog/sidecar/app.py +421 -60
- package/ocmemog/sidecar/compat.py +50 -13
- package/ocmemog/sidecar/transcript_watcher.py +327 -242
- package/openclaw.plugin.json +4 -0
- package/package.json +1 -1
- package/scripts/ocmemog-backfill-vectors.py +5 -3
- package/scripts/ocmemog-continuity-benchmark.py +1 -1
- package/scripts/ocmemog-demo.py +1 -1
- package/scripts/ocmemog-doctor.py +15 -0
- package/scripts/ocmemog-install.sh +29 -7
- package/scripts/ocmemog-integrated-proof.py +374 -0
- package/scripts/ocmemog-reindex-vectors.py +5 -3
- package/scripts/ocmemog-release-check.sh +330 -0
- package/scripts/ocmemog-sidecar.sh +4 -2
- package/scripts/ocmemog-test-rig.py +5 -3
- package/brain/runtime/memory/artifacts.py +0 -33
- package/brain/runtime/memory/context_builder.py +0 -112
- package/brain/runtime/memory/interaction_memory.py +0 -57
- package/brain/runtime/memory/memory_gate.py +0 -38
- package/brain/runtime/memory/memory_graph.py +0 -54
- package/brain/runtime/memory/person_identity.py +0 -83
- package/brain/runtime/memory/person_memory.py +0 -138
- package/brain/runtime/memory/sentiment_memory.py +0 -67
- package/brain/runtime/memory/tool_catalog.py +0 -68
|
@@ -1,188 +1,3 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from brain.runtime.instrumentation import emit_event
|
|
6
|
-
from brain.runtime import state_store
|
|
7
|
-
from brain.runtime.memory import store
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
EMBED_TABLES = tuple(store.MEMORY_TABLES)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def run_integrity_check() -> Dict[str, Any]:
|
|
14
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_integrity_start", status="ok")
|
|
15
|
-
conn = store.connect()
|
|
16
|
-
issues: List[str] = []
|
|
17
|
-
repairable: List[str] = []
|
|
18
|
-
sqlite_ok = True
|
|
19
|
-
|
|
20
|
-
# required tables
|
|
21
|
-
required = {
|
|
22
|
-
"experiences",
|
|
23
|
-
"knowledge",
|
|
24
|
-
"preferences",
|
|
25
|
-
"identity",
|
|
26
|
-
"reflections",
|
|
27
|
-
"tasks",
|
|
28
|
-
"directives",
|
|
29
|
-
"promotions",
|
|
30
|
-
"candidates",
|
|
31
|
-
"memory_index",
|
|
32
|
-
"vector_embeddings",
|
|
33
|
-
"runbooks",
|
|
34
|
-
"lessons",
|
|
35
|
-
}
|
|
36
|
-
tables = {row[0] for row in conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()}
|
|
37
|
-
missing = required - tables
|
|
38
|
-
if missing:
|
|
39
|
-
issues.append(f"missing_tables:{','.join(sorted(missing))}")
|
|
40
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_integrity_issue", status="warn")
|
|
41
|
-
|
|
42
|
-
# orphan candidates (source_event_id missing in experiences)
|
|
43
|
-
try:
|
|
44
|
-
orphan = conn.execute(
|
|
45
|
-
"SELECT COUNT(*) FROM candidates WHERE source_event_id NOT IN (SELECT id FROM experiences)",
|
|
46
|
-
).fetchone()[0]
|
|
47
|
-
if orphan:
|
|
48
|
-
issues.append(f"orphan_candidates:{orphan}")
|
|
49
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_integrity_issue", status="warn")
|
|
50
|
-
except Exception:
|
|
51
|
-
pass
|
|
52
|
-
|
|
53
|
-
# duplicate promotions
|
|
54
|
-
try:
|
|
55
|
-
dup_groups = conn.execute(
|
|
56
|
-
"SELECT COUNT(*) FROM (SELECT 1 FROM promotions GROUP BY source, content HAVING COUNT(*) > 1)",
|
|
57
|
-
).fetchone()[0]
|
|
58
|
-
if dup_groups:
|
|
59
|
-
issues.append(f"duplicate_promotions:{dup_groups}")
|
|
60
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_integrity_issue", status="warn")
|
|
61
|
-
except Exception:
|
|
62
|
-
pass
|
|
63
|
-
|
|
64
|
-
# reinforcement references missing
|
|
65
|
-
try:
|
|
66
|
-
missing_ref = conn.execute(
|
|
67
|
-
"SELECT COUNT(*) FROM experiences WHERE memory_reference IS NULL OR memory_reference = ''",
|
|
68
|
-
).fetchone()[0]
|
|
69
|
-
if missing_ref:
|
|
70
|
-
issues.append(f"missing_memory_reference:{missing_ref}")
|
|
71
|
-
repairable.append("missing_memory_reference")
|
|
72
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_integrity_issue", status="warn")
|
|
73
|
-
except Exception:
|
|
74
|
-
pass
|
|
75
|
-
|
|
76
|
-
# vector index mismatch (knowledge/runbooks/lessons vs vector_embeddings)
|
|
77
|
-
missing_vectors = 0
|
|
78
|
-
orphan_vectors = 0
|
|
79
|
-
try:
|
|
80
|
-
for table in EMBED_TABLES:
|
|
81
|
-
missing_vectors += conn.execute(
|
|
82
|
-
f"SELECT COUNT(*) FROM {table} WHERE id NOT IN (SELECT CAST(source_id AS INTEGER) FROM vector_embeddings WHERE source_type=?)",
|
|
83
|
-
(table,),
|
|
84
|
-
).fetchone()[0]
|
|
85
|
-
except Exception:
|
|
86
|
-
pass
|
|
87
|
-
|
|
88
|
-
try:
|
|
89
|
-
for table in EMBED_TABLES:
|
|
90
|
-
orphan_vectors += conn.execute(
|
|
91
|
-
"SELECT COUNT(*) FROM vector_embeddings WHERE source_type=? AND CAST(source_id AS INTEGER) NOT IN (SELECT id FROM %s)"
|
|
92
|
-
% table,
|
|
93
|
-
(table,),
|
|
94
|
-
).fetchone()[0]
|
|
95
|
-
except Exception:
|
|
96
|
-
pass
|
|
97
|
-
|
|
98
|
-
if missing_vectors:
|
|
99
|
-
issues.append(f"vector_missing:{missing_vectors}")
|
|
100
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_vector_integrity_issue", status="warn")
|
|
101
|
-
|
|
102
|
-
if orphan_vectors:
|
|
103
|
-
issues.append(f"vector_orphan:{orphan_vectors}")
|
|
104
|
-
repairable.append("vector_orphan")
|
|
105
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_vector_integrity_issue", status="warn")
|
|
106
|
-
|
|
107
|
-
try:
|
|
108
|
-
quick_check = conn.execute("PRAGMA quick_check(1)").fetchone()
|
|
109
|
-
quick_value = str((quick_check or ["ok"])[0] or "ok")
|
|
110
|
-
if quick_value.lower() != "ok":
|
|
111
|
-
sqlite_ok = False
|
|
112
|
-
issues.append(f"sqlite_quick_check:{quick_value}")
|
|
113
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_integrity_issue", status="warn")
|
|
114
|
-
except Exception:
|
|
115
|
-
sqlite_ok = False
|
|
116
|
-
|
|
117
|
-
warning_type = ""
|
|
118
|
-
warning_summary = ""
|
|
119
|
-
for issue in issues:
|
|
120
|
-
if issue.startswith("vector_missing"):
|
|
121
|
-
warning_type = "vector_missing"
|
|
122
|
-
warning_summary = "Vector embeddings missing entries"
|
|
123
|
-
break
|
|
124
|
-
if issue.startswith("vector_orphan"):
|
|
125
|
-
warning_type = "vector_orphan"
|
|
126
|
-
warning_summary = "Vector embeddings have orphan entries"
|
|
127
|
-
break
|
|
128
|
-
|
|
129
|
-
conn.close()
|
|
130
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_integrity_complete", status="ok")
|
|
131
|
-
return {
|
|
132
|
-
"issues": issues,
|
|
133
|
-
"ok": len(issues) == 0 and sqlite_ok,
|
|
134
|
-
"warning_type": warning_type,
|
|
135
|
-
"warning_summary": warning_summary,
|
|
136
|
-
"repairable_issues": repairable,
|
|
137
|
-
"sqlite_ok": sqlite_ok,
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def repair_integrity() -> Dict[str, Any]:
|
|
142
|
-
repaired: List[str] = []
|
|
143
|
-
|
|
144
|
-
def _write() -> Dict[str, Any]:
|
|
145
|
-
conn = store.connect()
|
|
146
|
-
removed_orphans = 0
|
|
147
|
-
repaired_missing_refs = 0
|
|
148
|
-
try:
|
|
149
|
-
tables = {row[0] for row in conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()}
|
|
150
|
-
if "vector_embeddings" in tables:
|
|
151
|
-
for table in EMBED_TABLES:
|
|
152
|
-
if table not in tables:
|
|
153
|
-
continue
|
|
154
|
-
removed_orphans += conn.execute(
|
|
155
|
-
f"""
|
|
156
|
-
DELETE FROM vector_embeddings
|
|
157
|
-
WHERE source_type = ?
|
|
158
|
-
AND NOT EXISTS (
|
|
159
|
-
SELECT 1 FROM {table} source
|
|
160
|
-
WHERE source.id = CAST(vector_embeddings.source_id AS INTEGER)
|
|
161
|
-
)
|
|
162
|
-
""",
|
|
163
|
-
(table,),
|
|
164
|
-
).rowcount
|
|
165
|
-
if "experiences" in tables:
|
|
166
|
-
repaired_missing_refs += conn.execute(
|
|
167
|
-
"""
|
|
168
|
-
UPDATE experiences
|
|
169
|
-
SET memory_reference = 'legacy:' || COALESCE(experience_type, 'unknown') || ':' || id
|
|
170
|
-
WHERE memory_reference IS NULL OR memory_reference = ''
|
|
171
|
-
"""
|
|
172
|
-
).rowcount
|
|
173
|
-
conn.commit()
|
|
174
|
-
return {
|
|
175
|
-
"removed_orphan_vectors": int(removed_orphans),
|
|
176
|
-
"repaired_missing_memory_references": int(repaired_missing_refs),
|
|
177
|
-
}
|
|
178
|
-
finally:
|
|
179
|
-
conn.close()
|
|
180
|
-
|
|
181
|
-
result = store.submit_write(_write, timeout=30.0)
|
|
182
|
-
if int(result.get("removed_orphan_vectors") or 0) > 0:
|
|
183
|
-
repaired.append(f"vector_orphan:{int(result['removed_orphan_vectors'])}")
|
|
184
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_integrity_repair", status="ok", repaired="vector_orphan", count=int(result["removed_orphan_vectors"]))
|
|
185
|
-
if int(result.get("repaired_missing_memory_references") or 0) > 0:
|
|
186
|
-
repaired.append(f"missing_memory_reference:{int(result['repaired_missing_memory_references'])}")
|
|
187
|
-
emit_event(state_store.reports_dir() / "brain_memory.log.jsonl", "brain_memory_integrity_repair", status="ok", repaired="missing_memory_reference", count=int(result["repaired_missing_memory_references"]))
|
|
188
|
-
return {"ok": True, "repaired": repaired, **result}
|
|
3
|
+
from ocmemog.runtime.memory.integrity import * # noqa: F401,F403
|
|
@@ -1,60 +1,3 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from brain.runtime import state_store
|
|
6
|
-
from brain.runtime.instrumentation import emit_event
|
|
7
|
-
from brain.runtime.memory import memory_taxonomy
|
|
8
|
-
|
|
9
|
-
LOGFILE = state_store.reports_dir() / "brain_memory.log.jsonl"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _cluster_key(record: Dict[str, object]) -> Tuple[str, str]:
|
|
13
|
-
mem_type = memory_taxonomy.classify_memory_type(record)
|
|
14
|
-
content = str(record.get("content") or "")
|
|
15
|
-
anchor = content[:48].lower()
|
|
16
|
-
return mem_type, anchor
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def consolidate_memories(records: List[Dict[str, object]], max_clusters: int = 5) -> Dict[str, object]:
|
|
20
|
-
emit_event(LOGFILE, "brain_memory_consolidation_start", status="ok")
|
|
21
|
-
clusters: Dict[Tuple[str, str], List[Dict[str, object]]] = {}
|
|
22
|
-
for record in records:
|
|
23
|
-
content = str(record.get("content") or "").strip()
|
|
24
|
-
if not content:
|
|
25
|
-
continue
|
|
26
|
-
key = _cluster_key(record)
|
|
27
|
-
clusters.setdefault(key, []).append(record)
|
|
28
|
-
if len(clusters) >= max_clusters:
|
|
29
|
-
break
|
|
30
|
-
consolidated: List[Dict[str, object]] = []
|
|
31
|
-
reinforcement_updates: List[Dict[str, object]] = []
|
|
32
|
-
for key, items in clusters.items():
|
|
33
|
-
mem_type, anchor = key
|
|
34
|
-
summary = f"{mem_type} cluster: {anchor}"
|
|
35
|
-
references = [str(item.get("reference") or "") for item in items if str(item.get("reference") or "")]
|
|
36
|
-
consolidated.append(
|
|
37
|
-
{
|
|
38
|
-
"memory_type": mem_type,
|
|
39
|
-
"summary": summary,
|
|
40
|
-
"count": len(items),
|
|
41
|
-
"references": references,
|
|
42
|
-
"candidate_kinds": sorted({str(item.get("candidate_kind") or "memory") for item in items}),
|
|
43
|
-
}
|
|
44
|
-
)
|
|
45
|
-
reinforcement_updates.append(
|
|
46
|
-
{
|
|
47
|
-
"memory_type": mem_type,
|
|
48
|
-
"weight": min(1.0, len(items) / 5.0),
|
|
49
|
-
"references": references,
|
|
50
|
-
}
|
|
51
|
-
)
|
|
52
|
-
emit_event(
|
|
53
|
-
LOGFILE,
|
|
54
|
-
"brain_memory_consolidation_cluster",
|
|
55
|
-
status="ok",
|
|
56
|
-
memory_type=mem_type,
|
|
57
|
-
count=len(items),
|
|
58
|
-
)
|
|
59
|
-
emit_event(LOGFILE, "brain_memory_consolidation_complete", status="ok", cluster_count=len(consolidated))
|
|
60
|
-
return {"consolidated": consolidated, "reinforcement": reinforcement_updates}
|
|
3
|
+
from ocmemog.runtime.memory.memory_consolidation import * # noqa: F401,F403
|
|
@@ -1,109 +1,3 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
from typing import Dict, List
|
|
5
|
-
|
|
6
|
-
from brain.runtime import state_store
|
|
7
|
-
from brain.runtime.instrumentation import emit_event
|
|
8
|
-
from brain.runtime.memory import store
|
|
9
|
-
|
|
10
|
-
LOGFILE = state_store.reports_dir() / "brain_memory.log.jsonl"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _dedupe_memory_links(conn) -> None:
|
|
14
|
-
conn.execute(
|
|
15
|
-
"""
|
|
16
|
-
DELETE FROM memory_links
|
|
17
|
-
WHERE rowid NOT IN (
|
|
18
|
-
SELECT MIN(rowid)
|
|
19
|
-
FROM memory_links
|
|
20
|
-
GROUP BY source_reference, link_type, target_reference
|
|
21
|
-
)
|
|
22
|
-
"""
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _ensure_table(conn) -> None:
|
|
27
|
-
conn.execute(
|
|
28
|
-
"""
|
|
29
|
-
CREATE TABLE IF NOT EXISTS memory_links (
|
|
30
|
-
source_reference TEXT NOT NULL,
|
|
31
|
-
link_type TEXT NOT NULL,
|
|
32
|
-
target_reference TEXT NOT NULL,
|
|
33
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
34
|
-
UNIQUE(source_reference, link_type, target_reference)
|
|
35
|
-
)
|
|
36
|
-
"""
|
|
37
|
-
)
|
|
38
|
-
try:
|
|
39
|
-
conn.execute(
|
|
40
|
-
"CREATE UNIQUE INDEX IF NOT EXISTS idx_memory_links_unique ON memory_links(source_reference, link_type, target_reference)"
|
|
41
|
-
)
|
|
42
|
-
except sqlite3.IntegrityError:
|
|
43
|
-
_dedupe_memory_links(conn)
|
|
44
|
-
conn.execute("DROP INDEX IF EXISTS idx_memory_links_unique")
|
|
45
|
-
conn.execute(
|
|
46
|
-
"CREATE UNIQUE INDEX IF NOT EXISTS idx_memory_links_unique ON memory_links(source_reference, link_type, target_reference)"
|
|
47
|
-
)
|
|
48
|
-
conn.commit()
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def add_memory_link(source_reference: str, link_type: str, target_reference: str) -> None:
|
|
52
|
-
conn = store.connect()
|
|
53
|
-
_ensure_table(conn)
|
|
54
|
-
conn.execute(
|
|
55
|
-
"INSERT OR IGNORE INTO memory_links (source_reference, link_type, target_reference) VALUES (?, ?, ?)",
|
|
56
|
-
(source_reference, link_type, target_reference),
|
|
57
|
-
)
|
|
58
|
-
conn.commit()
|
|
59
|
-
conn.close()
|
|
60
|
-
emit_event(LOGFILE, "brain_memory_link_created", status="ok", link_type=link_type)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def get_memory_links(source_reference: str) -> List[Dict[str, str]]:
|
|
64
|
-
conn = store.connect()
|
|
65
|
-
_ensure_table(conn)
|
|
66
|
-
rows = conn.execute(
|
|
67
|
-
"SELECT link_type, target_reference FROM memory_links WHERE source_reference=?",
|
|
68
|
-
(source_reference,),
|
|
69
|
-
).fetchall()
|
|
70
|
-
conn.close()
|
|
71
|
-
return [{"link_type": row[0], "target_reference": row[1]} for row in rows]
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def get_memory_links_for_target(target_reference: str) -> List[Dict[str, str]]:
|
|
75
|
-
conn = store.connect()
|
|
76
|
-
_ensure_table(conn)
|
|
77
|
-
rows = conn.execute(
|
|
78
|
-
"SELECT source_reference, link_type, target_reference FROM memory_links WHERE target_reference=? ORDER BY created_at DESC",
|
|
79
|
-
(target_reference,),
|
|
80
|
-
).fetchall()
|
|
81
|
-
conn.close()
|
|
82
|
-
return [
|
|
83
|
-
{
|
|
84
|
-
"source_reference": row[0],
|
|
85
|
-
"link_type": row[1],
|
|
86
|
-
"target_reference": row[2],
|
|
87
|
-
}
|
|
88
|
-
for row in rows
|
|
89
|
-
]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def get_memory_links_for_thread(thread_id: str) -> List[Dict[str, str]]:
|
|
93
|
-
return get_memory_links_for_target(f"thread:{thread_id}")
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def get_memory_links_for_session(session_id: str) -> List[Dict[str, str]]:
|
|
97
|
-
return get_memory_links_for_target(f"session:{session_id}")
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def get_memory_links_for_conversation(conversation_id: str) -> List[Dict[str, str]]:
|
|
101
|
-
return get_memory_links_for_target(f"conversation:{conversation_id}")
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def count_memory_links() -> int:
|
|
105
|
-
conn = store.connect()
|
|
106
|
-
_ensure_table(conn)
|
|
107
|
-
row = conn.execute("SELECT COUNT(*) FROM memory_links").fetchone()
|
|
108
|
-
conn.close()
|
|
109
|
-
return int(row[0]) if row else 0
|
|
3
|
+
from ocmemog.runtime.memory.memory_links import * # noqa: F401,F403
|
|
@@ -1,235 +1,3 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from brain.runtime import state_store
|
|
6
|
-
from brain.runtime.instrumentation import emit_event
|
|
7
|
-
from brain.runtime.memory import freshness
|
|
8
|
-
|
|
9
|
-
LOGFILE = state_store.reports_dir() / "brain_memory.log.jsonl"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def score_salience(record: Mapping[str, float]) -> Dict[str, float | bool]:
|
|
13
|
-
importance = float(record.get("importance", 0.2))
|
|
14
|
-
novelty = float(record.get("novelty", 0.1))
|
|
15
|
-
uncertainty = float(record.get("uncertainty", 0.1))
|
|
16
|
-
risk = float(record.get("risk", 0.0))
|
|
17
|
-
goal_alignment = float(record.get("goal_alignment", 0.1))
|
|
18
|
-
reinforcement = float(record.get("reinforcement", 0.0))
|
|
19
|
-
user_interest = float(record.get("user_interest", 0.0))
|
|
20
|
-
recency = float(record.get("freshness", 0.0))
|
|
21
|
-
signal_priority = float(record.get("signal_priority", 0.0))
|
|
22
|
-
salience_score = max(
|
|
23
|
-
0.0,
|
|
24
|
-
min(3.0, importance + novelty + uncertainty + risk + goal_alignment + reinforcement + user_interest + recency + signal_priority),
|
|
25
|
-
)
|
|
26
|
-
activation_strength = min(1.0, salience_score / 3.0)
|
|
27
|
-
attention_trigger = salience_score >= 1.5
|
|
28
|
-
emit_event(LOGFILE, "brain_memory_salience_scored", status="ok", score=salience_score)
|
|
29
|
-
emit_event(LOGFILE, "brain_memory_salience_updated", status="ok", score=salience_score)
|
|
30
|
-
return {
|
|
31
|
-
"salience_score": round(salience_score, 3),
|
|
32
|
-
"activation_strength": round(activation_strength, 3),
|
|
33
|
-
"attention_trigger": attention_trigger,
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _as_float(value: Any, default: float = 0.0) -> float:
|
|
38
|
-
try:
|
|
39
|
-
return float(value)
|
|
40
|
-
except Exception:
|
|
41
|
-
return default
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def _content_text(record: Mapping[str, Any]) -> str:
|
|
45
|
-
return str(record.get("effective_content") or record.get("content") or "").strip()
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _normalized_position(record_id: int, *, latest_id: int, earliest_id: int) -> float:
|
|
49
|
-
if latest_id <= earliest_id:
|
|
50
|
-
return 1.0
|
|
51
|
-
return max(0.0, min(1.0, (record_id - earliest_id) / float(latest_id - earliest_id)))
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def score_turn_salience(
|
|
55
|
-
turn: Mapping[str, Any],
|
|
56
|
-
*,
|
|
57
|
-
latest_turn_id: int | None = None,
|
|
58
|
-
earliest_turn_id: int | None = None,
|
|
59
|
-
active_branch_id: str | None = None,
|
|
60
|
-
reply_chain_turn_ids: Sequence[int] | None = None,
|
|
61
|
-
) -> Dict[str, Any]:
|
|
62
|
-
metadata = turn.get("metadata") if isinstance(turn.get("metadata"), dict) else {}
|
|
63
|
-
resolution = metadata.get("resolution") if isinstance(metadata.get("resolution"), dict) else {}
|
|
64
|
-
role = str(turn.get("role") or "")
|
|
65
|
-
content = _content_text(turn)
|
|
66
|
-
turn_id = int(turn.get("id") or 0)
|
|
67
|
-
branch_id = str(metadata.get("branch_id") or "")
|
|
68
|
-
latest_turn_id = int(latest_turn_id or turn_id or 1)
|
|
69
|
-
earliest_turn_id = int(earliest_turn_id or turn_id or 1)
|
|
70
|
-
reply_chain_ids = {int(item) for item in (reply_chain_turn_ids or []) if int(item or 0) > 0}
|
|
71
|
-
freshness_score = _normalized_position(turn_id or earliest_turn_id, latest_id=latest_turn_id, earliest_id=earliest_turn_id)
|
|
72
|
-
|
|
73
|
-
importance = 0.55 if role == "user" else 0.35
|
|
74
|
-
novelty = 0.25 if metadata.get("branch_depth") == 0 and branch_id else 0.1
|
|
75
|
-
uncertainty = 0.45 if "?" in content else 0.0
|
|
76
|
-
risk = 0.35 if resolution.get("decision") == "decline" else 0.0
|
|
77
|
-
goal_alignment = 0.45 if role == "user" else 0.0
|
|
78
|
-
if resolution:
|
|
79
|
-
goal_alignment += 0.2
|
|
80
|
-
if any(token in content.lower() for token in ("i will", "i'll", "let me", "next", "need to", "please", "can you")):
|
|
81
|
-
goal_alignment += 0.2
|
|
82
|
-
reinforcement = 0.2 if resolution.get("decision") == "confirm" else 0.0
|
|
83
|
-
user_interest = 0.3 if role == "user" else 0.0
|
|
84
|
-
signal_priority = 0.0
|
|
85
|
-
if active_branch_id and branch_id and branch_id == active_branch_id:
|
|
86
|
-
signal_priority += 0.45
|
|
87
|
-
if turn_id and turn_id in reply_chain_ids:
|
|
88
|
-
signal_priority += 0.35
|
|
89
|
-
if metadata.get("reply_to_turn_id"):
|
|
90
|
-
signal_priority += 0.1
|
|
91
|
-
|
|
92
|
-
scored = score_salience(
|
|
93
|
-
{
|
|
94
|
-
"importance": importance,
|
|
95
|
-
"novelty": novelty,
|
|
96
|
-
"uncertainty": uncertainty,
|
|
97
|
-
"risk": risk,
|
|
98
|
-
"goal_alignment": min(goal_alignment, 0.8),
|
|
99
|
-
"reinforcement": reinforcement,
|
|
100
|
-
"user_interest": user_interest,
|
|
101
|
-
"freshness": freshness_score,
|
|
102
|
-
"signal_priority": min(signal_priority, 0.9),
|
|
103
|
-
}
|
|
104
|
-
)
|
|
105
|
-
return {
|
|
106
|
-
**dict(scored),
|
|
107
|
-
"reference": turn.get("reference"),
|
|
108
|
-
"id": turn.get("id"),
|
|
109
|
-
"role": role,
|
|
110
|
-
"content": content,
|
|
111
|
-
"branch_id": branch_id or None,
|
|
112
|
-
"resolution": resolution or None,
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def score_checkpoint_salience(
|
|
117
|
-
checkpoint: Mapping[str, Any],
|
|
118
|
-
*,
|
|
119
|
-
latest_checkpoint_id: int | None = None,
|
|
120
|
-
active_branch_id: str | None = None,
|
|
121
|
-
) -> Dict[str, Any]:
|
|
122
|
-
metadata = checkpoint.get("metadata") if isinstance(checkpoint.get("metadata"), dict) else {}
|
|
123
|
-
active_branch = metadata.get("active_branch") if isinstance(metadata.get("active_branch"), dict) else {}
|
|
124
|
-
checkpoint_id = int(checkpoint.get("id") or 0)
|
|
125
|
-
latest_checkpoint_id = int(latest_checkpoint_id or checkpoint_id or 1)
|
|
126
|
-
freshness_score = 1.0 if latest_checkpoint_id <= 0 else max(0.0, min(1.0, checkpoint_id / float(latest_checkpoint_id or 1)))
|
|
127
|
-
open_loops = checkpoint.get("open_loops") if isinstance(checkpoint.get("open_loops"), list) else []
|
|
128
|
-
pending_actions = checkpoint.get("pending_actions") if isinstance(checkpoint.get("pending_actions"), list) else []
|
|
129
|
-
latest_user_ask = str(checkpoint.get("latest_user_ask") or "").strip()
|
|
130
|
-
commitment = str(checkpoint.get("last_assistant_commitment") or "").strip()
|
|
131
|
-
|
|
132
|
-
importance = 0.45 + min(0.3, len(open_loops) * 0.08)
|
|
133
|
-
novelty = 0.1 + (0.15 if int(checkpoint.get("depth") or 0) == 0 else 0.0)
|
|
134
|
-
uncertainty = 0.3 if "?" in latest_user_ask else 0.0
|
|
135
|
-
risk = min(0.45, len(pending_actions) * 0.08)
|
|
136
|
-
goal_alignment = 0.25 + (0.2 if latest_user_ask else 0.0) + (0.15 if commitment else 0.0)
|
|
137
|
-
reinforcement = 0.0
|
|
138
|
-
user_interest = 0.25 if latest_user_ask else 0.0
|
|
139
|
-
signal_priority = 0.0
|
|
140
|
-
if active_branch_id and str(active_branch.get("branch_id") or "") == active_branch_id:
|
|
141
|
-
signal_priority += 0.35
|
|
142
|
-
if open_loops:
|
|
143
|
-
signal_priority += 0.25
|
|
144
|
-
|
|
145
|
-
scored = score_salience(
|
|
146
|
-
{
|
|
147
|
-
"importance": min(importance, 0.8),
|
|
148
|
-
"novelty": novelty,
|
|
149
|
-
"uncertainty": uncertainty,
|
|
150
|
-
"risk": risk,
|
|
151
|
-
"goal_alignment": min(goal_alignment, 0.8),
|
|
152
|
-
"reinforcement": reinforcement,
|
|
153
|
-
"user_interest": user_interest,
|
|
154
|
-
"freshness": freshness_score,
|
|
155
|
-
"signal_priority": min(signal_priority, 0.8),
|
|
156
|
-
}
|
|
157
|
-
)
|
|
158
|
-
return {
|
|
159
|
-
**dict(scored),
|
|
160
|
-
"reference": checkpoint.get("reference"),
|
|
161
|
-
"id": checkpoint.get("id"),
|
|
162
|
-
"summary": str(checkpoint.get("summary") or "").strip(),
|
|
163
|
-
"active_branch_id": active_branch.get("branch_id"),
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
def rank_turns_by_salience(
|
|
168
|
-
turns: Sequence[Mapping[str, Any]],
|
|
169
|
-
*,
|
|
170
|
-
active_branch_id: str | None = None,
|
|
171
|
-
reply_chain_turn_ids: Sequence[int] | None = None,
|
|
172
|
-
limit: int | None = None,
|
|
173
|
-
) -> List[Dict[str, Any]]:
|
|
174
|
-
turns_list = list(turns)
|
|
175
|
-
if not turns_list:
|
|
176
|
-
return []
|
|
177
|
-
ids = [int(item.get("id") or 0) for item in turns_list if int(item.get("id") or 0) > 0]
|
|
178
|
-
latest_turn_id = max(ids) if ids else 1
|
|
179
|
-
earliest_turn_id = min(ids) if ids else latest_turn_id
|
|
180
|
-
ranked = []
|
|
181
|
-
for turn in turns_list:
|
|
182
|
-
salience = score_turn_salience(
|
|
183
|
-
turn,
|
|
184
|
-
latest_turn_id=latest_turn_id,
|
|
185
|
-
earliest_turn_id=earliest_turn_id,
|
|
186
|
-
active_branch_id=active_branch_id,
|
|
187
|
-
reply_chain_turn_ids=reply_chain_turn_ids,
|
|
188
|
-
)
|
|
189
|
-
ranked.append({"turn": dict(turn), "salience": salience})
|
|
190
|
-
ranked.sort(
|
|
191
|
-
key=lambda item: (
|
|
192
|
-
_as_float(item["salience"].get("salience_score")),
|
|
193
|
-
_as_float((item["turn"] or {}).get("id")),
|
|
194
|
-
),
|
|
195
|
-
reverse=True,
|
|
196
|
-
)
|
|
197
|
-
return ranked[: limit or len(ranked)]
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def rank_checkpoints_by_salience(
|
|
201
|
-
checkpoints: Sequence[Mapping[str, Any]],
|
|
202
|
-
*,
|
|
203
|
-
active_branch_id: str | None = None,
|
|
204
|
-
limit: int | None = None,
|
|
205
|
-
) -> List[Dict[str, Any]]:
|
|
206
|
-
checkpoint_list = list(checkpoints)
|
|
207
|
-
if not checkpoint_list:
|
|
208
|
-
return []
|
|
209
|
-
latest_checkpoint_id = max(int(item.get("id") or 0) for item in checkpoint_list) or 1
|
|
210
|
-
ranked = []
|
|
211
|
-
for checkpoint in checkpoint_list:
|
|
212
|
-
salience = score_checkpoint_salience(
|
|
213
|
-
checkpoint,
|
|
214
|
-
latest_checkpoint_id=latest_checkpoint_id,
|
|
215
|
-
active_branch_id=active_branch_id,
|
|
216
|
-
)
|
|
217
|
-
ranked.append({"checkpoint": dict(checkpoint), "salience": salience})
|
|
218
|
-
ranked.sort(
|
|
219
|
-
key=lambda item: (
|
|
220
|
-
_as_float(item["salience"].get("salience_score")),
|
|
221
|
-
_as_float((item["checkpoint"] or {}).get("id")),
|
|
222
|
-
),
|
|
223
|
-
reverse=True,
|
|
224
|
-
)
|
|
225
|
-
return ranked[: limit or len(ranked)]
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def scan_salient_memories(limit: int = 5) -> List[Dict[str, float | bool]]:
|
|
229
|
-
advisories = freshness.scan_freshness(limit=limit).get("advisories", [])
|
|
230
|
-
results = []
|
|
231
|
-
for item in advisories:
|
|
232
|
-
score = score_salience({"freshness": float(item.get("freshness_score", 0.0))})
|
|
233
|
-
if score.get("attention_trigger"):
|
|
234
|
-
results.append(score)
|
|
235
|
-
return results[:limit]
|
|
3
|
+
from ocmemog.runtime.memory.memory_salience import * # noqa: F401,F403
|
|
@@ -1,33 +1,3 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from brain.runtime import state_store
|
|
6
|
-
from brain.runtime.instrumentation import emit_event
|
|
7
|
-
from brain.runtime.memory import reinforcement
|
|
8
|
-
|
|
9
|
-
LOGFILE = state_store.reports_dir() / "brain_memory.log.jsonl"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
SYNTHESIS_TYPES = [
|
|
13
|
-
"theme_summary",
|
|
14
|
-
"user_preference",
|
|
15
|
-
"candidate_procedure",
|
|
16
|
-
"recurring_pattern",
|
|
17
|
-
"contradiction_candidate",
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def synthesize_memory_patterns(limit: int = 5) -> List[Dict[str, str]]:
|
|
22
|
-
emit_event(LOGFILE, "brain_memory_synthesis_start", status="ok")
|
|
23
|
-
stats = reinforcement.list_recent_experiences(limit=limit)
|
|
24
|
-
results: List[Dict[str, str]] = []
|
|
25
|
-
for key, count in stats.items():
|
|
26
|
-
results.append(
|
|
27
|
-
{
|
|
28
|
-
"type": "recurring_pattern",
|
|
29
|
-
"summary": f"{key} occurred {count} times",
|
|
30
|
-
}
|
|
31
|
-
)
|
|
32
|
-
emit_event(LOGFILE, "brain_memory_synthesis_complete", status="ok", count=len(results))
|
|
33
|
-
return results[:limit]
|
|
3
|
+
from ocmemog.runtime.memory.memory_synthesis import * # noqa: F401,F403
|
|
@@ -1,35 +1,3 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from brain.runtime import state_store
|
|
6
|
-
from brain.runtime.instrumentation import emit_event
|
|
7
|
-
|
|
8
|
-
LOGFILE = state_store.reports_dir() / "brain_memory.log.jsonl"
|
|
9
|
-
|
|
10
|
-
MEMORY_TYPES = [
|
|
11
|
-
"episodic",
|
|
12
|
-
"semantic",
|
|
13
|
-
"procedural",
|
|
14
|
-
"relationship",
|
|
15
|
-
"working",
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def list_memory_types() -> List[str]:
|
|
20
|
-
return list(MEMORY_TYPES)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def classify_memory_type(record: Dict[str, object]) -> str:
|
|
24
|
-
content = str(record.get("content") or "")
|
|
25
|
-
memory_type = "semantic"
|
|
26
|
-
if "how to" in content.lower() or "step" in content.lower():
|
|
27
|
-
memory_type = "procedural"
|
|
28
|
-
elif "met" in content.lower() or "relationship" in content.lower():
|
|
29
|
-
memory_type = "relationship"
|
|
30
|
-
elif record.get("memory_type") in MEMORY_TYPES:
|
|
31
|
-
memory_type = str(record.get("memory_type"))
|
|
32
|
-
elif record.get("source") == "working":
|
|
33
|
-
memory_type = "working"
|
|
34
|
-
emit_event(LOGFILE, "brain_memory_taxonomy_assigned", status="ok", memory_type=memory_type)
|
|
35
|
-
return memory_type
|
|
3
|
+
from ocmemog.runtime.memory.memory_taxonomy import * # noqa: F401,F403
|