@simbimbo/memory-ocmemog 0.1.15 → 0.1.17

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.
@@ -4,9 +4,10 @@ import importlib
4
4
  import importlib.util
5
5
  import os
6
6
  from dataclasses import dataclass
7
+ from pathlib import Path
7
8
  from typing import Any
8
9
 
9
- from ocmemog.runtime import identity
10
+ from ocmemog.runtime import config, identity, state_store
10
11
 
11
12
 
12
13
  @dataclass(frozen=True)
@@ -17,6 +18,7 @@ class RuntimeStatus:
17
18
  warnings: list[str]
18
19
  identity: dict[str, Any]
19
20
  capabilities: list[dict[str, Any]]
21
+ runtime_summary: dict[str, Any]
20
22
 
21
23
 
22
24
  TODO_ITEMS = [
@@ -36,6 +38,121 @@ _EMBEDDING_PROVIDER_BACKEND_HINTS = {
36
38
  }
37
39
 
38
40
 
41
+ def _parse_agent_id_list(raw: str | None) -> list[str]:
42
+ return [item.strip() for item in str(raw or "").split(",") if item.strip()]
43
+
44
+
45
+ def _queue_runtime_summary() -> dict[str, Any]:
46
+ queue_path = state_store.data_dir() / "ingest_queue.jsonl"
47
+ stats_path = state_store.data_dir() / "queue_stats.json"
48
+ depth = 0
49
+ invalid_lines = 0
50
+ retrying_lines = 0
51
+ max_retry_seen = 0
52
+ try:
53
+ if queue_path.exists():
54
+ import json
55
+
56
+ with queue_path.open("r", encoding="utf-8", errors="ignore") as handle:
57
+ for raw_line in handle:
58
+ line = raw_line.strip()
59
+ if not line:
60
+ continue
61
+ depth += 1
62
+ try:
63
+ payload = json.loads(line)
64
+ if isinstance(payload, dict):
65
+ retry_count = int(payload.get("_ocmemog_retry_count", 0) or 0)
66
+ if retry_count > 0:
67
+ retrying_lines += 1
68
+ max_retry_seen = max(max_retry_seen, retry_count)
69
+ except Exception:
70
+ invalid_lines += 1
71
+ except Exception:
72
+ depth = 0
73
+ invalid_lines = 0
74
+ retrying_lines = 0
75
+ max_retry_seen = 0
76
+
77
+ stats: dict[str, Any] = {}
78
+ try:
79
+ if stats_path.exists():
80
+ import json
81
+
82
+ parsed = json.loads(stats_path.read_text(encoding="utf-8"))
83
+ if isinstance(parsed, dict):
84
+ stats = parsed
85
+ except Exception:
86
+ stats = {}
87
+
88
+ worker_enabled = str(os.environ.get("OCMEMOG_INGEST_ASYNC_WORKER", "false")).strip().lower() in {"1", "true", "yes"}
89
+ error_count = int(stats.get("errors") or 0)
90
+ config_issues: list[str] = []
91
+ try:
92
+ worker_poll_seconds = float(os.environ.get("OCMEMOG_INGEST_ASYNC_POLL_SECONDS", "5"))
93
+ if worker_poll_seconds < 0:
94
+ config_issues.append("OCMEMOG_INGEST_ASYNC_POLL_SECONDS must be >= 0")
95
+ except Exception:
96
+ config_issues.append("OCMEMOG_INGEST_ASYNC_POLL_SECONDS")
97
+ try:
98
+ worker_batch_max = int(os.environ.get("OCMEMOG_INGEST_ASYNC_BATCH_MAX", "25"))
99
+ if worker_batch_max < 1:
100
+ config_issues.append("OCMEMOG_INGEST_ASYNC_BATCH_MAX must be >= 1")
101
+ except Exception:
102
+ config_issues.append("OCMEMOG_INGEST_ASYNC_BATCH_MAX")
103
+ hints: list[str] = []
104
+ severity = "ok"
105
+ backlog_severity = "low"
106
+ if config_issues:
107
+ severity = "warn"
108
+ hints.append("queue worker config has invalid values")
109
+ if depth > 0 and not worker_enabled:
110
+ severity = "warn"
111
+ hints.append("queue has backlog but async worker is disabled")
112
+ if error_count > 0:
113
+ severity = "warn"
114
+ hints.append("queue has recorded ingest/parse errors")
115
+ if invalid_lines > 0:
116
+ severity = "warn"
117
+ hints.append("queue contains invalid lines")
118
+ if retrying_lines > 0:
119
+ severity = "warn"
120
+ hints.append("queue contains retrying payloads")
121
+ if depth > 1000:
122
+ backlog_severity = "critical"
123
+ elif depth > 100:
124
+ backlog_severity = "high"
125
+ elif depth > 25:
126
+ backlog_severity = "medium"
127
+ if depth > 100:
128
+ severity = "high"
129
+ hints.append("queue backlog is high")
130
+ elif depth > 25 and severity == "ok":
131
+ severity = "warn"
132
+ hints.append("queue backlog is elevated")
133
+
134
+ return {
135
+ "enabled": worker_enabled,
136
+ "status": severity,
137
+ "issues": config_issues,
138
+ "depth": int(depth),
139
+ "queue_depth": int(depth),
140
+ "queue_backlog_severity": backlog_severity,
141
+ "last_run": stats.get("last_run"),
142
+ "last_batch": int(stats.get("last_batch") or 0),
143
+ "processed_total": int(stats.get("processed") or 0),
144
+ "error_count": error_count,
145
+ "last_error": stats.get("last_error"),
146
+ "invalid_lines": int(invalid_lines),
147
+ "retrying_lines": int(retrying_lines),
148
+ "max_retry_seen": int(max_retry_seen),
149
+ "worker_enabled": worker_enabled,
150
+ "config_issues": config_issues,
151
+ "severity": severity,
152
+ "hints": hints,
153
+ }
154
+
155
+
39
156
  def probe_runtime() -> RuntimeStatus:
40
157
  runtime_identity = identity.get_runtime_identity()
41
158
  capabilities = runtime_identity.get("capabilities", [])
@@ -59,7 +176,16 @@ def probe_runtime() -> RuntimeStatus:
59
176
  or os.environ.get("OCMEMOG_EMBED_PROVIDER", "")
60
177
  or os.environ.get("BRAIN_EMBED_MODEL_PROVIDER", "")
61
178
  ).strip().lower()
62
- if importlib.util.find_spec("sentence_transformers") is None and provider not in _EMBEDDING_PROVIDER_BACKEND_HINTS:
179
+ local_model = str(
180
+ getattr(config, "OCMEMOG_EMBED_MODEL_LOCAL", "")
181
+ or getattr(config, "BRAIN_EMBED_MODEL_LOCAL", getattr(config, "OCMEMOG_EMBED_LOCAL_MODEL", "simple"))
182
+ or ""
183
+ ).strip().lower()
184
+ sentence_transformers_ready = importlib.util.find_spec("sentence_transformers") is not None
185
+ local_simple_only = local_model in {"", "simple", "hash"}
186
+ provider_configured = provider in _EMBEDDING_PROVIDER_BACKEND_HINTS
187
+ using_hash_embeddings = bool(not provider_configured and local_model in {"", "simple", "hash"} and not sentence_transformers_ready)
188
+ if not sentence_transformers_ready and provider not in _EMBEDDING_PROVIDER_BACKEND_HINTS:
63
189
  warnings.append("Optional dependency missing: sentence-transformers; using local hash embeddings.")
64
190
 
65
191
  try:
@@ -81,6 +207,37 @@ def probe_runtime() -> RuntimeStatus:
81
207
 
82
208
  if missing_deps:
83
209
  mode = "degraded"
210
+
211
+ hydration_allow_agents = _parse_agent_id_list(os.environ.get("OCMEMOG_AUTO_HYDRATION_ALLOW_AGENT_IDS"))
212
+ hydration_deny_agents = _parse_agent_id_list(os.environ.get("OCMEMOG_AUTO_HYDRATION_DENY_AGENT_IDS"))
213
+ runtime_summary = {
214
+ "mode": mode,
215
+ "embedding_provider": provider or "local-simple",
216
+ "embedding_local_model": local_model or "simple",
217
+ "embedding_path_summary": {
218
+ "enabled": True,
219
+ "status": "provider" if provider_configured else ("local-simple" if using_hash_embeddings else "local"),
220
+ "issues": ["sentence-transformers missing"] if (not sentence_transformers_ready and not provider_configured) else [],
221
+ "provider_configured": provider_configured,
222
+ "provider_backend_hint": provider if provider else None,
223
+ "local_model": local_model or "simple",
224
+ "local_simple_only": local_simple_only,
225
+ "sentence_transformers_ready": sentence_transformers_ready,
226
+ },
227
+ "using_hash_embeddings": using_hash_embeddings,
228
+ "shim_surface_count": shim_count,
229
+ "missing_dep_count": len(missing_deps),
230
+ "warning_count": len(warnings),
231
+ "queue": _queue_runtime_summary(),
232
+ "auto_hydration": {
233
+ "enabled": str(os.environ.get("OCMEMOG_AUTO_HYDRATION", "false")).strip().lower() in {"1", "true", "yes"},
234
+ "status": "enabled" if str(os.environ.get("OCMEMOG_AUTO_HYDRATION", "false")).strip().lower() in {"1", "true", "yes"} else "disabled",
235
+ "issues": [],
236
+ "allow_agent_ids": hydration_allow_agents,
237
+ "deny_agent_ids": hydration_deny_agents,
238
+ "scoped_by_agent": bool(hydration_allow_agents or hydration_deny_agents),
239
+ },
240
+ }
84
241
  return RuntimeStatus(
85
242
  mode=mode,
86
243
  missing_deps=missing_deps,
@@ -88,6 +245,7 @@ def probe_runtime() -> RuntimeStatus:
88
245
  warnings=warnings,
89
246
  identity=runtime_identity,
90
247
  capabilities=capabilities,
248
+ runtime_summary=runtime_summary,
91
249
  )
92
250
 
93
251
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simbimbo/memory-ocmemog",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Advanced OpenClaw memory plugin with durable recall, transcript-backed continuity, and sidecar APIs",
5
5
  "license": "MIT",
6
6
  "repository": {