@simbimbo/memory-ocmemog 0.1.11 → 0.1.12

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 (102) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +83 -18
  3. package/brain/runtime/__init__.py +2 -12
  4. package/brain/runtime/config.py +1 -24
  5. package/brain/runtime/inference.py +1 -151
  6. package/brain/runtime/instrumentation.py +1 -15
  7. package/brain/runtime/memory/__init__.py +3 -13
  8. package/brain/runtime/memory/api.py +1 -1219
  9. package/brain/runtime/memory/candidate.py +1 -185
  10. package/brain/runtime/memory/conversation_state.py +1 -1823
  11. package/brain/runtime/memory/distill.py +1 -344
  12. package/brain/runtime/memory/embedding_engine.py +1 -92
  13. package/brain/runtime/memory/freshness.py +1 -112
  14. package/brain/runtime/memory/health.py +1 -40
  15. package/brain/runtime/memory/integrity.py +1 -186
  16. package/brain/runtime/memory/memory_consolidation.py +1 -58
  17. package/brain/runtime/memory/memory_links.py +1 -107
  18. package/brain/runtime/memory/memory_salience.py +1 -233
  19. package/brain/runtime/memory/memory_synthesis.py +1 -31
  20. package/brain/runtime/memory/memory_taxonomy.py +1 -33
  21. package/brain/runtime/memory/pondering_engine.py +1 -654
  22. package/brain/runtime/memory/promote.py +1 -277
  23. package/brain/runtime/memory/provenance.py +1 -406
  24. package/brain/runtime/memory/reinforcement.py +1 -71
  25. package/brain/runtime/memory/retrieval.py +1 -210
  26. package/brain/runtime/memory/semantic_search.py +1 -64
  27. package/brain/runtime/memory/store.py +1 -429
  28. package/brain/runtime/memory/unresolved_state.py +1 -91
  29. package/brain/runtime/memory/vector_index.py +1 -323
  30. package/brain/runtime/model_roles.py +1 -9
  31. package/brain/runtime/model_router.py +1 -22
  32. package/brain/runtime/providers.py +1 -66
  33. package/brain/runtime/security/redaction.py +1 -12
  34. package/brain/runtime/state_store.py +1 -23
  35. package/brain/runtime/storage_paths.py +1 -39
  36. package/docs/architecture/memory.md +20 -24
  37. package/docs/release-checklist.md +19 -6
  38. package/docs/usage.md +33 -17
  39. package/index.ts +8 -1
  40. package/ocmemog/__init__.py +11 -0
  41. package/ocmemog/doctor.py +1255 -0
  42. package/ocmemog/runtime/__init__.py +18 -0
  43. package/ocmemog/runtime/_compat_bridge.py +28 -0
  44. package/ocmemog/runtime/config.py +35 -0
  45. package/ocmemog/runtime/identity.py +115 -0
  46. package/ocmemog/runtime/inference.py +164 -0
  47. package/ocmemog/runtime/instrumentation.py +20 -0
  48. package/ocmemog/runtime/memory/__init__.py +91 -0
  49. package/ocmemog/runtime/memory/api.py +1431 -0
  50. package/ocmemog/runtime/memory/candidate.py +192 -0
  51. package/ocmemog/runtime/memory/conversation_state.py +1831 -0
  52. package/ocmemog/runtime/memory/distill.py +282 -0
  53. package/ocmemog/runtime/memory/embedding_engine.py +151 -0
  54. package/ocmemog/runtime/memory/freshness.py +114 -0
  55. package/ocmemog/runtime/memory/health.py +57 -0
  56. package/ocmemog/runtime/memory/integrity.py +208 -0
  57. package/ocmemog/runtime/memory/memory_consolidation.py +60 -0
  58. package/ocmemog/runtime/memory/memory_links.py +109 -0
  59. package/ocmemog/runtime/memory/memory_salience.py +235 -0
  60. package/ocmemog/runtime/memory/memory_synthesis.py +33 -0
  61. package/ocmemog/runtime/memory/memory_taxonomy.py +35 -0
  62. package/ocmemog/runtime/memory/pondering_engine.py +681 -0
  63. package/ocmemog/runtime/memory/promote.py +279 -0
  64. package/ocmemog/runtime/memory/provenance.py +408 -0
  65. package/ocmemog/runtime/memory/reinforcement.py +73 -0
  66. package/ocmemog/runtime/memory/retrieval.py +224 -0
  67. package/ocmemog/runtime/memory/semantic_search.py +66 -0
  68. package/ocmemog/runtime/memory/store.py +433 -0
  69. package/ocmemog/runtime/memory/unresolved_state.py +93 -0
  70. package/ocmemog/runtime/memory/vector_index.py +411 -0
  71. package/ocmemog/runtime/model_roles.py +16 -0
  72. package/ocmemog/runtime/model_router.py +29 -0
  73. package/ocmemog/runtime/providers.py +79 -0
  74. package/ocmemog/runtime/roles.py +92 -0
  75. package/ocmemog/runtime/security/__init__.py +8 -0
  76. package/ocmemog/runtime/security/redaction.py +17 -0
  77. package/ocmemog/runtime/state_store.py +34 -0
  78. package/ocmemog/runtime/storage_paths.py +70 -0
  79. package/ocmemog/sidecar/app.py +310 -23
  80. package/ocmemog/sidecar/compat.py +50 -13
  81. package/ocmemog/sidecar/transcript_watcher.py +318 -240
  82. package/openclaw.plugin.json +4 -0
  83. package/package.json +1 -1
  84. package/scripts/ocmemog-backfill-vectors.py +5 -3
  85. package/scripts/ocmemog-continuity-benchmark.py +1 -1
  86. package/scripts/ocmemog-demo.py +1 -1
  87. package/scripts/ocmemog-doctor.py +15 -0
  88. package/scripts/ocmemog-install.sh +29 -7
  89. package/scripts/ocmemog-integrated-proof.py +373 -0
  90. package/scripts/ocmemog-reindex-vectors.py +5 -3
  91. package/scripts/ocmemog-release-check.sh +330 -0
  92. package/scripts/ocmemog-sidecar.sh +4 -2
  93. package/scripts/ocmemog-test-rig.py +5 -3
  94. package/brain/runtime/memory/artifacts.py +0 -33
  95. package/brain/runtime/memory/context_builder.py +0 -112
  96. package/brain/runtime/memory/interaction_memory.py +0 -57
  97. package/brain/runtime/memory/memory_gate.py +0 -38
  98. package/brain/runtime/memory/memory_graph.py +0 -54
  99. package/brain/runtime/memory/person_identity.py +0 -83
  100. package/brain/runtime/memory/person_memory.py +0 -138
  101. package/brain/runtime/memory/sentiment_memory.py +0 -67
  102. package/brain/runtime/memory/tool_catalog.py +0 -68
@@ -1,83 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Dict, Tuple
4
- import re
5
-
6
- from brain.runtime import state_store
7
- from brain.runtime.instrumentation import emit_event
8
- from brain.runtime.memory import person_memory
9
-
10
- LOGFILE = state_store.reports_dir() / "brain_memory.log.jsonl"
11
-
12
-
13
- def extract_intro_candidate(text: str) -> str | None:
14
- if not isinstance(text, str):
15
- return None
16
- cleaned = text.strip()
17
- if not cleaned:
18
- return None
19
- lowered = cleaned.lower().strip(".!")
20
- call_me_match = re.search(r"\byou can call me\s+([a-z][a-z\-']*)", lowered)
21
- if call_me_match:
22
- return call_me_match.group(1).strip(" .!").title()
23
- direct_call_match = re.search(r"\bcall me\s+([a-z][a-z\-']*)", lowered)
24
- if direct_call_match:
25
- return direct_call_match.group(1).strip(" .!").title()
26
- patterns = [
27
- r"^my name is (.+)$",
28
- r"^i am (.+)$",
29
- r"^i'm (.+)$",
30
- ]
31
- for pattern in patterns:
32
- match = re.match(pattern, lowered)
33
- if match:
34
- candidate = match.group(1).split(" but ")[0].strip(" .!")
35
- return candidate.title() if candidate else None
36
- return None
37
-
38
-
39
- def extract_name_candidate(text: str) -> str | None:
40
- candidate = extract_intro_candidate(text)
41
- if candidate:
42
- return candidate
43
- if not isinstance(text, str):
44
- return None
45
- cleaned = text.strip()
46
- if not cleaned:
47
- return None
48
- tokens = [token for token in cleaned.replace(".", "").split() if token]
49
- if 1 <= len(tokens) <= 3:
50
- return " ".join(tokens)
51
- return None
52
-
53
-
54
- def extract_operator_name(text: str) -> str | None:
55
- from brain.runtime import inference
56
-
57
- llm_result = inference.parse_operator_name(text)
58
- llm_name = llm_result.get("name") if isinstance(llm_result, dict) else ""
59
- if isinstance(llm_name, str) and llm_name.strip():
60
- return llm_name.strip()
61
- return extract_name_candidate(text)
62
-
63
-
64
- def resolve_interaction_person(metadata: Dict[str, str]) -> Tuple[Dict[str, object] | None, float, bool]:
65
- person = None
66
- confidence = 0.0
67
- name_input = metadata.get("name") or ""
68
- name_candidate = extract_name_candidate(name_input) if name_input else None
69
- if name_candidate:
70
- person = person_memory.get_person(name_candidate) or person_memory.create_person(name_candidate, name_candidate)
71
- confidence = 0.7
72
- if not person and metadata.get("email"):
73
- person = person_memory.find_person_by_email(metadata["email"])
74
- confidence = 0.6 if person else 0.0
75
- if not person and metadata.get("phone"):
76
- person = person_memory.find_person_by_phone(metadata["phone"])
77
- confidence = 0.6 if person else 0.0
78
- ask_name_required = confidence < 0.5
79
- if person:
80
- emit_event(LOGFILE, "brain_person_identity_resolved", status="ok", person_id=person.get("person_id"))
81
- else:
82
- emit_event(LOGFILE, "brain_person_identity_uncertain", status="ok")
83
- return person, confidence, ask_name_required
@@ -1,138 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import sqlite3
5
- import time
6
- from typing import Dict, List
7
-
8
- from brain.runtime import state_store
9
- from brain.runtime.instrumentation import emit_event
10
-
11
- LOGFILE = state_store.reports_dir() / "brain_memory.log.jsonl"
12
-
13
-
14
- def _connect() -> sqlite3.Connection:
15
- path = state_store.data_dir() / "person_memory.db"
16
- path.parent.mkdir(parents=True, exist_ok=True)
17
- conn = sqlite3.connect(str(path))
18
- conn.row_factory = sqlite3.Row
19
- conn.execute(
20
- """
21
- CREATE TABLE IF NOT EXISTS person_memory (
22
- person_id TEXT PRIMARY KEY,
23
- display_name TEXT,
24
- aliases_json TEXT,
25
- email_addresses_json TEXT,
26
- phone_numbers_json TEXT,
27
- interaction_count INTEGER DEFAULT 0,
28
- trust_score REAL DEFAULT 0.5,
29
- trust_level TEXT DEFAULT 'operator',
30
- expertise_tags_json TEXT,
31
- communication_style_json TEXT,
32
- last_seen TEXT,
33
- relationship_type TEXT,
34
- notes_json TEXT,
35
- created_at TEXT
36
- )
37
- """
38
- )
39
- _ensure_columns(conn)
40
- return conn
41
-
42
-
43
- def _ensure_columns(conn: sqlite3.Connection) -> None:
44
- existing = {row[1] for row in conn.execute("PRAGMA table_info(person_memory)")}
45
- columns = {
46
- "trust_level": "ALTER TABLE person_memory ADD COLUMN trust_level TEXT DEFAULT 'operator'",
47
- "expertise_tags_json": "ALTER TABLE person_memory ADD COLUMN expertise_tags_json TEXT",
48
- "communication_style_json": "ALTER TABLE person_memory ADD COLUMN communication_style_json TEXT",
49
- "relationship_type": "ALTER TABLE person_memory ADD COLUMN relationship_type TEXT",
50
- "notes_json": "ALTER TABLE person_memory ADD COLUMN notes_json TEXT",
51
- "created_at": "ALTER TABLE person_memory ADD COLUMN created_at TEXT",
52
- }
53
- for name, ddl in columns.items():
54
- if name not in existing:
55
- conn.execute(ddl)
56
-
57
-
58
- def create_person(person_id: str, display_name: str = "") -> Dict[str, object]:
59
- conn = _connect()
60
- conn.execute(
61
- """
62
- INSERT OR IGNORE INTO person_memory (
63
- person_id, display_name, aliases_json, email_addresses_json, phone_numbers_json,
64
- interaction_count, trust_score, trust_level, expertise_tags_json, communication_style_json,
65
- last_seen, relationship_type, notes_json
66
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
67
- """,
68
- (
69
- person_id,
70
- display_name,
71
- json.dumps([]),
72
- json.dumps([]),
73
- json.dumps([]),
74
- 0,
75
- 0.5,
76
- "operator",
77
- json.dumps([]),
78
- json.dumps({}),
79
- time.strftime("%Y-%m-%d %H:%M:%S"),
80
- "",
81
- json.dumps([]),
82
- time.strftime("%Y-%m-%d %H:%M:%S"),
83
- ),
84
- )
85
- conn.commit()
86
- conn.close()
87
- emit_event(LOGFILE, "brain_person_memory_created", status="ok", person_id=person_id)
88
- return get_person(person_id) or {}
89
-
90
-
91
- def get_person(person_id: str) -> Dict[str, object] | None:
92
- conn = _connect()
93
- row = conn.execute("SELECT * FROM person_memory WHERE person_id=?", (person_id,)).fetchone()
94
- conn.close()
95
- return dict(row) if row else None
96
-
97
-
98
- def update_person(person_id: str, updates: Dict[str, object]) -> None:
99
- conn = _connect()
100
- fields = []
101
- values = []
102
- for key, value in updates.items():
103
- fields.append(f"{key}=?")
104
- values.append(value)
105
- if not fields:
106
- conn.close()
107
- return
108
- values.append(person_id)
109
- conn.execute(f"UPDATE person_memory SET {', '.join(fields)} WHERE person_id=?", values)
110
- conn.commit()
111
- conn.close()
112
- emit_event(LOGFILE, "brain_person_memory_updated", status="ok", person_id=person_id)
113
-
114
-
115
- def _find_by_json(field: str, value: str) -> Dict[str, object] | None:
116
- conn = _connect()
117
- rows = conn.execute("SELECT * FROM person_memory").fetchall()
118
- conn.close()
119
- for row in rows:
120
- payload = json.loads(row[field] or "[]")
121
- if value in payload:
122
- return dict(row)
123
- return None
124
-
125
-
126
- def find_person_by_email(email: str) -> Dict[str, object] | None:
127
- return _find_by_json("email_addresses_json", email)
128
-
129
-
130
- def find_person_by_phone(phone: str) -> Dict[str, object] | None:
131
- return _find_by_json("phone_numbers_json", phone)
132
-
133
-
134
- def list_people(limit: int = 50) -> List[Dict[str, object]]:
135
- conn = _connect()
136
- rows = conn.execute("SELECT * FROM person_memory ORDER BY last_seen DESC LIMIT ?", (limit,)).fetchall()
137
- conn.close()
138
- return [dict(row) for row in rows]
@@ -1,67 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import sqlite3
4
- import time
5
- from typing import Dict, List
6
-
7
- from brain.runtime import state_store
8
- from brain.runtime.instrumentation import emit_event
9
- from brain.runtime.memory import person_memory
10
-
11
- LOGFILE = state_store.reports_dir() / "brain_memory.log.jsonl"
12
-
13
- SENTIMENTS = {"positive", "neutral", "negative", "frustrated", "urgent", "excited"}
14
-
15
-
16
- def classify_sentiment(text: str) -> str:
17
- lowered = (text or "").lower()
18
- if "urgent" in lowered:
19
- return "urgent"
20
- if "frustrated" in lowered or "angry" in lowered:
21
- return "frustrated"
22
- if "excited" in lowered or "great" in lowered:
23
- return "excited"
24
- if "bad" in lowered:
25
- return "negative"
26
- if "good" in lowered:
27
- return "positive"
28
- return "neutral"
29
-
30
-
31
- def _connect() -> sqlite3.Connection:
32
- path = state_store.data_dir() / "sentiment_memory.db"
33
- path.parent.mkdir(parents=True, exist_ok=True)
34
- conn = sqlite3.connect(str(path))
35
- conn.row_factory = sqlite3.Row
36
- conn.execute(
37
- """
38
- CREATE TABLE IF NOT EXISTS sentiment_memory (
39
- person_id TEXT,
40
- sentiment TEXT,
41
- timestamp TEXT
42
- )
43
- """
44
- )
45
- return conn
46
-
47
-
48
- def update_person_sentiment_baseline(person_id: str, sentiment: str) -> None:
49
- sentiment = sentiment if sentiment in SENTIMENTS else "neutral"
50
- conn = _connect()
51
- conn.execute(
52
- "INSERT INTO sentiment_memory (person_id, sentiment, timestamp) VALUES (?, ?, ?)",
53
- (person_id, sentiment, time.strftime("%Y-%m-%d %H:%M:%S")),
54
- )
55
- conn.commit()
56
- conn.close()
57
- emit_event(LOGFILE, "brain_person_sentiment_updated", status="ok", person_id=person_id)
58
-
59
-
60
- def list_sentiment(person_id: str, limit: int = 10) -> List[Dict[str, str]]:
61
- conn = _connect()
62
- rows = conn.execute(
63
- "SELECT sentiment, timestamp FROM sentiment_memory WHERE person_id=? ORDER BY timestamp DESC LIMIT ?",
64
- (person_id, limit),
65
- ).fetchall()
66
- conn.close()
67
- return [dict(row) for row in rows]
@@ -1,68 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- from typing import Dict, Any
5
-
6
- from brain.runtime.instrumentation import emit_event
7
- from brain.runtime import state_store
8
- from brain.runtime.memory import store
9
-
10
-
11
- def _ensure_table(conn) -> None:
12
- conn.execute(
13
- """
14
- CREATE TABLE IF NOT EXISTS tool_catalog (
15
- tool_id TEXT PRIMARY KEY,
16
- description TEXT,
17
- permission_class TEXT,
18
- capability_tags TEXT,
19
- first_seen TEXT DEFAULT (datetime('now')),
20
- last_used TEXT
21
- )
22
- """
23
- )
24
-
25
-
26
- def record_tool_metadata(metadata: Dict[str, Any]) -> None:
27
- conn = store.connect()
28
- _ensure_table(conn)
29
- tool_id = metadata.get("tool_id")
30
- description = metadata.get("description", "")
31
- permission_class = metadata.get("permission_class", "restricted")
32
- capability_tags = json.dumps(metadata.get("capability_tags", []) or [])
33
- conn.execute(
34
- """
35
- INSERT INTO tool_catalog (tool_id, description, permission_class, capability_tags)
36
- VALUES (?, ?, ?, ?)
37
- ON CONFLICT(tool_id) DO UPDATE SET
38
- description=excluded.description,
39
- permission_class=excluded.permission_class,
40
- capability_tags=excluded.capability_tags
41
- """,
42
- (tool_id, description, permission_class, capability_tags),
43
- )
44
- conn.commit()
45
- conn.close()
46
- emit_event(
47
- state_store.reports_dir() / "brain_memory.log.jsonl",
48
- "brain_memory_tool_catalog_update",
49
- status="ok",
50
- tool_id=tool_id,
51
- )
52
-
53
-
54
- def record_tool_usage(tool_id: str) -> None:
55
- conn = store.connect()
56
- _ensure_table(conn)
57
- conn.execute(
58
- "UPDATE tool_catalog SET last_used=datetime('now') WHERE tool_id=?",
59
- (tool_id,),
60
- )
61
- conn.commit()
62
- conn.close()
63
- emit_event(
64
- state_store.reports_dir() / "brain_memory.log.jsonl",
65
- "brain_memory_tool_catalog_update",
66
- status="ok",
67
- tool_id=tool_id,
68
- )