@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
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""ocmemog-native runtime namespace with explicit local module ownership."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from . import config, inference, instrumentation, identity, model_roles, model_router, providers, roles, state_store, storage_paths
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"config",
|
|
9
|
+
"identity",
|
|
10
|
+
"inference",
|
|
11
|
+
"instrumentation",
|
|
12
|
+
"model_roles",
|
|
13
|
+
"model_router",
|
|
14
|
+
"providers",
|
|
15
|
+
"roles",
|
|
16
|
+
"state_store",
|
|
17
|
+
"storage_paths",
|
|
18
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Internal helpers for explicitly wrapping legacy `brain.runtime` modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import sys
|
|
7
|
+
import types
|
|
8
|
+
from types import ModuleType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def wrap_legacy_module(alias: str, legacy_name: str) -> ModuleType:
|
|
12
|
+
"""Return a module object exposing `legacy_name` under `alias`.
|
|
13
|
+
|
|
14
|
+
This keeps import semantics stable (`import ocmemog.runtime.xxx`) while preserving
|
|
15
|
+
a native module identity for ocmemog surfaces.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
legacy = importlib.import_module(legacy_name)
|
|
19
|
+
module = types.ModuleType(alias)
|
|
20
|
+
module.__dict__.update(legacy.__dict__)
|
|
21
|
+
module.__dict__["__name__"] = alias
|
|
22
|
+
module.__dict__["__package__"] = alias.rpartition(".")[0]
|
|
23
|
+
module.__dict__["__wrapped_from__"] = legacy_name
|
|
24
|
+
module.__dict__["__wrapped_module__"] = legacy
|
|
25
|
+
module.__dict__["__wrapped_by__"] = "ocmemog-runtime-bridge"
|
|
26
|
+
sys.modules[alias] = module
|
|
27
|
+
return module
|
|
28
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Runtime configuration surface owned by ocmemog."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
OCMEMOG_EMBED_MODEL_LOCAL = os.environ.get("OCMEMOG_EMBED_MODEL_LOCAL", "")
|
|
9
|
+
OCMEMOG_EMBED_LOCAL_MODEL = OCMEMOG_EMBED_MODEL_LOCAL or os.environ.get("BRAIN_EMBED_MODEL_LOCAL", "simple")
|
|
10
|
+
BRAIN_EMBED_MODEL_LOCAL = OCMEMOG_EMBED_LOCAL_MODEL
|
|
11
|
+
OCMEMOG_EMBED_MODEL_PROVIDER = os.environ.get("OCMEMOG_EMBED_MODEL_PROVIDER", "")
|
|
12
|
+
OCMEMOG_EMBED_PROVIDER = OCMEMOG_EMBED_MODEL_PROVIDER or os.environ.get("BRAIN_EMBED_MODEL_PROVIDER", "")
|
|
13
|
+
BRAIN_EMBED_MODEL_PROVIDER = OCMEMOG_EMBED_PROVIDER
|
|
14
|
+
|
|
15
|
+
OCMEMOG_MEMORY_MODEL = os.environ.get("OCMEMOG_MEMORY_MODEL", "gpt-4o-mini")
|
|
16
|
+
OCMEMOG_OPENAI_API_BASE = os.environ.get("OCMEMOG_OPENAI_API_BASE", "https://api.openai.com/v1")
|
|
17
|
+
OCMEMOG_OPENAI_EMBED_MODEL = os.environ.get("OCMEMOG_OPENAI_EMBED_MODEL", "text-embedding-3-small")
|
|
18
|
+
|
|
19
|
+
OCMEMOG_LOCAL_LLM_BASE_URL = os.environ.get("OCMEMOG_LOCAL_LLM_BASE_URL", "http://127.0.0.1:18080/v1")
|
|
20
|
+
OCMEMOG_LOCAL_LLM_MODEL = os.environ.get("OCMEMOG_LOCAL_LLM_MODEL", "qwen2.5-7b-instruct")
|
|
21
|
+
OCMEMOG_LOCAL_EMBED_BASE_URL = os.environ.get("OCMEMOG_LOCAL_EMBED_BASE_URL", "http://127.0.0.1:18081/v1")
|
|
22
|
+
OCMEMOG_LOCAL_EMBED_MODEL = os.environ.get("OCMEMOG_LOCAL_EMBED_MODEL", "nomic-embed-text-v1.5")
|
|
23
|
+
|
|
24
|
+
OCMEMOG_OLLAMA_HOST = os.environ.get("OCMEMOG_OLLAMA_HOST", "http://127.0.0.1:11434")
|
|
25
|
+
OCMEMOG_OLLAMA_MODEL = os.environ.get("OCMEMOG_OLLAMA_MODEL", "qwen2.5:7b")
|
|
26
|
+
OCMEMOG_OLLAMA_EMBED_MODEL = os.environ.get("OCMEMOG_OLLAMA_EMBED_MODEL", "nomic-embed-text:latest")
|
|
27
|
+
|
|
28
|
+
OCMEMOG_PROMOTION_THRESHOLD = float(os.environ.get("OCMEMOG_PROMOTION_THRESHOLD", "0.5"))
|
|
29
|
+
OCMEMOG_DEMOTION_THRESHOLD = float(os.environ.get("OCMEMOG_DEMOTION_THRESHOLD", "0.2"))
|
|
30
|
+
|
|
31
|
+
OCMEMOG_PONDER_ENABLED = os.environ.get("OCMEMOG_PONDER_ENABLED", "true")
|
|
32
|
+
OCMEMOG_PONDER_MODEL = os.environ.get("OCMEMOG_PONDER_MODEL", OCMEMOG_MEMORY_MODEL)
|
|
33
|
+
OCMEMOG_LESSON_MINING_ENABLED = os.environ.get("OCMEMOG_LESSON_MINING_ENABLED", "true")
|
|
34
|
+
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Runtime identity and capability ownership metadata for ocmemog runtime surfaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections import OrderedDict
|
|
6
|
+
from typing import Any, Dict, List
|
|
7
|
+
|
|
8
|
+
from importlib import import_module
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
ENGINE_ID = "ocmemog-native"
|
|
12
|
+
SURFACE_ENGINE_OWNER = "ocmemog-native"
|
|
13
|
+
SURFACE_COMPAT_OWNER = "brain-runtime-shim"
|
|
14
|
+
SURFACE_MISSING = "missing"
|
|
15
|
+
|
|
16
|
+
# Ordered list keeps output stable for deterministic status payloads.
|
|
17
|
+
_RUNTIME_SURFACES = (
|
|
18
|
+
"ocmemog.runtime.roles",
|
|
19
|
+
"ocmemog.runtime.identity",
|
|
20
|
+
"ocmemog.runtime.config",
|
|
21
|
+
"ocmemog.runtime.inference",
|
|
22
|
+
"ocmemog.runtime.model_router",
|
|
23
|
+
"ocmemog.runtime.model_roles",
|
|
24
|
+
"ocmemog.runtime.providers",
|
|
25
|
+
"ocmemog.runtime.state_store",
|
|
26
|
+
"ocmemog.runtime.storage_paths",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
_SURFACE_DETAILS = {
|
|
30
|
+
"ocmemog.runtime.roles": "Native role registry for context/priority metadata",
|
|
31
|
+
"ocmemog.runtime.identity": "Identity and capability ownership manifest",
|
|
32
|
+
"ocmemog.runtime.config": "Configuration surface for memory/model providers",
|
|
33
|
+
"ocmemog.runtime.inference": "Inference orchestration helper",
|
|
34
|
+
"ocmemog.runtime.model_router": "Model routing helper",
|
|
35
|
+
"ocmemog.runtime.model_roles": "Model-role mapping helper",
|
|
36
|
+
"ocmemog.runtime.providers": "Provider execution surface for embeddings",
|
|
37
|
+
"ocmemog.runtime.state_store": "Runtime state persistence surface",
|
|
38
|
+
"ocmemog.runtime.storage_paths": "Storage path helpers",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _surface_owner(module_name: str) -> tuple[str, str, str]:
|
|
43
|
+
"""Return (owner, provider_module, status) for an import target."""
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
module = import_module(module_name)
|
|
47
|
+
except Exception as exc: # pragma: no cover - defensive surface.
|
|
48
|
+
return (SURFACE_MISSING, "<import-failed>", f"{type(exc).__name__}: {exc}")
|
|
49
|
+
|
|
50
|
+
wrapped_from = getattr(module, "__wrapped_from__", None)
|
|
51
|
+
wrapped_by = getattr(module, "__wrapped_by__", None)
|
|
52
|
+
|
|
53
|
+
provider_module = module.__name__
|
|
54
|
+
owner = SURFACE_ENGINE_OWNER
|
|
55
|
+
|
|
56
|
+
if isinstance(wrapped_from, str) and wrapped_from.strip():
|
|
57
|
+
owner = SURFACE_COMPAT_OWNER
|
|
58
|
+
provider_module = wrapped_from.strip()
|
|
59
|
+
elif isinstance(wrapped_by, str) and wrapped_by.strip():
|
|
60
|
+
owner = SURFACE_COMPAT_OWNER
|
|
61
|
+
elif not provider_module.startswith("ocmemog."):
|
|
62
|
+
owner = SURFACE_COMPAT_OWNER
|
|
63
|
+
|
|
64
|
+
return (owner, provider_module, "ok")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_capability_ownership() -> List[Dict[str, Any]]:
|
|
68
|
+
"""Return a stable capability ownership matrix for runtime surfaces."""
|
|
69
|
+
|
|
70
|
+
owned: List[Dict[str, Any]] = []
|
|
71
|
+
for surface in _RUNTIME_SURFACES:
|
|
72
|
+
owner, provider_module, status = _surface_owner(surface)
|
|
73
|
+
owned.append(
|
|
74
|
+
{
|
|
75
|
+
"surface": surface,
|
|
76
|
+
"provider_module": provider_module,
|
|
77
|
+
"owner": owner,
|
|
78
|
+
"status": "ready" if owner != SURFACE_MISSING else "missing",
|
|
79
|
+
"status_detail": status,
|
|
80
|
+
"description": _SURFACE_DETAILS.get(surface, "runtime surface"),
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
return owned
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_runtime_identity() -> Dict[str, Any]:
|
|
87
|
+
"""Return a compact runtime-identity payload."""
|
|
88
|
+
|
|
89
|
+
capabilities = get_capability_ownership()
|
|
90
|
+
grouped = OrderedDict([
|
|
91
|
+
(SURFACE_ENGINE_OWNER, []),
|
|
92
|
+
(SURFACE_COMPAT_OWNER, []),
|
|
93
|
+
(SURFACE_MISSING, []),
|
|
94
|
+
])
|
|
95
|
+
for item in capabilities:
|
|
96
|
+
grouped.setdefault(item["owner"], []).append(item["surface"])
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
"engine": ENGINE_ID,
|
|
100
|
+
"schema": "ocmemog-runtime-identity-v1",
|
|
101
|
+
"capabilities": capabilities,
|
|
102
|
+
"capability_counts": {
|
|
103
|
+
SURFACE_ENGINE_OWNER: len(grouped[SURFACE_ENGINE_OWNER]),
|
|
104
|
+
SURFACE_COMPAT_OWNER: len(grouped[SURFACE_COMPAT_OWNER]),
|
|
105
|
+
SURFACE_MISSING: len(grouped[SURFACE_MISSING]),
|
|
106
|
+
},
|
|
107
|
+
"surface_names": list(_RUNTIME_SURFACES),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
__all__ = [
|
|
112
|
+
"ENGINE_ID",
|
|
113
|
+
"get_capability_ownership",
|
|
114
|
+
"get_runtime_identity",
|
|
115
|
+
]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""LLM inference helpers owned by ocmemog."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import urllib.request
|
|
9
|
+
|
|
10
|
+
from . import config, instrumentation, state_store
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
LOGFILE = state_store.report_log_path()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _infer_openai_compatible(
|
|
17
|
+
prompt: str,
|
|
18
|
+
*,
|
|
19
|
+
base_url: str,
|
|
20
|
+
model: str,
|
|
21
|
+
api_key: str | None = None,
|
|
22
|
+
provider_label: str = "openai-compatible",
|
|
23
|
+
) -> dict[str, str]:
|
|
24
|
+
url = f"{base_url.rstrip('/')}/chat/completions"
|
|
25
|
+
payload = {
|
|
26
|
+
"model": model,
|
|
27
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
28
|
+
"temperature": 0.2,
|
|
29
|
+
}
|
|
30
|
+
data = json.dumps(payload).encode("utf-8")
|
|
31
|
+
req = urllib.request.Request(url, data=data, method="POST")
|
|
32
|
+
if api_key:
|
|
33
|
+
req.add_header("Authorization", f"Bearer {api_key}")
|
|
34
|
+
req.add_header("Content-Type", "application/json")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
38
|
+
response = json.loads(resp.read().decode("utf-8"))
|
|
39
|
+
except Exception as exc:
|
|
40
|
+
instrumentation.emit_event(LOGFILE, "brain_infer_error", status="error", provider=provider_label, error=str(exc))
|
|
41
|
+
return {"status": "error", "error": f"request_failed:{exc}"}
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
output = response["choices"][0]["message"]["content"]
|
|
45
|
+
except Exception as exc:
|
|
46
|
+
instrumentation.emit_event(LOGFILE, "brain_infer_error", status="error", provider=provider_label, error=str(exc))
|
|
47
|
+
return {"status": "error", "error": "invalid_response"}
|
|
48
|
+
|
|
49
|
+
return {"status": "ok", "output": str(output).strip()}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _infer_ollama(prompt: str, model: str | None = None) -> dict[str, str]:
|
|
53
|
+
payload = {
|
|
54
|
+
"model": model or config.OCMEMOG_OLLAMA_MODEL,
|
|
55
|
+
"prompt": prompt,
|
|
56
|
+
"stream": False,
|
|
57
|
+
}
|
|
58
|
+
data = json.dumps(payload).encode("utf-8")
|
|
59
|
+
req = urllib.request.Request(f"{config.OCMEMOG_OLLAMA_HOST.rstrip('/')}/api/generate", data=data, method="POST")
|
|
60
|
+
req.add_header("Content-Type", "application/json")
|
|
61
|
+
try:
|
|
62
|
+
with urllib.request.urlopen(req, timeout=60) as resp:
|
|
63
|
+
response = json.loads(resp.read().decode("utf-8"))
|
|
64
|
+
except Exception as exc:
|
|
65
|
+
instrumentation.emit_event(LOGFILE, "brain_infer_error", status="error", provider="ollama", error=str(exc))
|
|
66
|
+
return {"status": "error", "error": f"ollama_failed:{exc}"}
|
|
67
|
+
output = response.get("response")
|
|
68
|
+
if not output:
|
|
69
|
+
instrumentation.emit_event(LOGFILE, "brain_infer_error", status="error", provider="ollama", error="invalid_response")
|
|
70
|
+
return {"status": "error", "error": "invalid_response"}
|
|
71
|
+
return {"status": "ok", "output": str(output).strip()}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _looks_like_local_openai_model(name: str) -> bool:
|
|
75
|
+
if not name:
|
|
76
|
+
return False
|
|
77
|
+
lowered = name.strip().lower()
|
|
78
|
+
return lowered.startswith("local-openai:") or lowered.startswith("local_openai:") or lowered.startswith("llamacpp:")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _normalize_local_model_name(name: str) -> str:
|
|
82
|
+
lowered = (name or "").strip()
|
|
83
|
+
for prefix in ("local-openai:", "local_openai:", "llamacpp:"):
|
|
84
|
+
if lowered.lower().startswith(prefix):
|
|
85
|
+
return lowered[len(prefix):]
|
|
86
|
+
return lowered
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _looks_like_ollama_model(name: str) -> bool:
|
|
90
|
+
if not name:
|
|
91
|
+
return False
|
|
92
|
+
lowered = name.strip().lower()
|
|
93
|
+
if lowered.startswith("ollama:"):
|
|
94
|
+
return True
|
|
95
|
+
if "/" in lowered:
|
|
96
|
+
return False
|
|
97
|
+
return ":" in lowered
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def stats() -> dict[str, object]:
|
|
101
|
+
materialized_local = int(_LOCAL_INFER_STATS.get("local_success", 0)) + int(_LOCAL_INFER_STATS.get("cache_hits", 0))
|
|
102
|
+
est_prompt_tokens_saved = materialized_local * _AVG_PROMPT_TOKENS_SAVED
|
|
103
|
+
est_completion_tokens_saved = materialized_local * _AVG_COMPLETION_TOKENS_SAVED
|
|
104
|
+
est_cost_saved = (
|
|
105
|
+
(est_prompt_tokens_saved / 1000.0) * _EST_FRONTIER_INPUT_COST_PER_1K
|
|
106
|
+
+ (est_completion_tokens_saved / 1000.0) * _EST_FRONTIER_OUTPUT_COST_PER_1K
|
|
107
|
+
)
|
|
108
|
+
return {
|
|
109
|
+
"cache_entries": len(_LOCAL_INFER_CACHE),
|
|
110
|
+
"warm_models": sorted(_MODEL_WARM_STATE.keys()),
|
|
111
|
+
"frontier_calls_avoided_est": materialized_local,
|
|
112
|
+
"prompt_tokens_saved_est": est_prompt_tokens_saved,
|
|
113
|
+
"completion_tokens_saved_est": est_completion_tokens_saved,
|
|
114
|
+
"cost_saved_usd_est": round(est_cost_saved, 4),
|
|
115
|
+
**{k: int(v) for k, v in _LOCAL_INFER_STATS.items()},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def infer(prompt: str, provider_name: str | None = None) -> dict[str, str]:
|
|
120
|
+
if not isinstance(prompt, str) or not prompt.strip():
|
|
121
|
+
return {"status": "error", "error": "empty_prompt"}
|
|
122
|
+
|
|
123
|
+
use_ollama = os.environ.get("OCMEMOG_USE_OLLAMA", "").lower() in {"1", "true", "yes"}
|
|
124
|
+
model_override = provider_name or config.OCMEMOG_MEMORY_MODEL
|
|
125
|
+
if _looks_like_local_openai_model(model_override):
|
|
126
|
+
model = _normalize_local_model_name(model_override) or config.OCMEMOG_LOCAL_LLM_MODEL
|
|
127
|
+
return _infer_openai_compatible(
|
|
128
|
+
prompt,
|
|
129
|
+
base_url=config.OCMEMOG_LOCAL_LLM_BASE_URL,
|
|
130
|
+
model=model,
|
|
131
|
+
api_key=os.environ.get("OCMEMOG_LOCAL_LLM_API_KEY") or os.environ.get("LOCAL_LLM_API_KEY"),
|
|
132
|
+
provider_label="local-openai",
|
|
133
|
+
)
|
|
134
|
+
if use_ollama or _looks_like_ollama_model(model_override):
|
|
135
|
+
model = model_override.split(":", 1)[-1] if model_override.startswith("ollama:") else model_override
|
|
136
|
+
return _infer_ollama(prompt, model)
|
|
137
|
+
|
|
138
|
+
api_key = os.environ.get("OCMEMOG_OPENAI_API_KEY") or os.environ.get("OPENAI_API_KEY")
|
|
139
|
+
if not api_key:
|
|
140
|
+
return _infer_openai_compatible(
|
|
141
|
+
prompt,
|
|
142
|
+
base_url=config.OCMEMOG_LOCAL_LLM_BASE_URL,
|
|
143
|
+
model=config.OCMEMOG_LOCAL_LLM_MODEL,
|
|
144
|
+
api_key=os.environ.get("OCMEMOG_LOCAL_LLM_API_KEY") or os.environ.get("LOCAL_LLM_API_KEY"),
|
|
145
|
+
provider_label="local-openai",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
model = model_override
|
|
149
|
+
return _infer_openai_compatible(
|
|
150
|
+
prompt,
|
|
151
|
+
base_url=config.OCMEMOG_OPENAI_API_BASE,
|
|
152
|
+
model=model,
|
|
153
|
+
api_key=api_key,
|
|
154
|
+
provider_label="openai",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def parse_operator_name(text: str) -> dict[str, str] | None:
|
|
159
|
+
match = re.search(r"\bmy name is ([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\b", text, flags=re.IGNORECASE)
|
|
160
|
+
if not match:
|
|
161
|
+
return None
|
|
162
|
+
return {"name": match.group(1)}
|
|
163
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Event emission utility for runtime instrumentation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def emit_event(path: Path, event: str, **fields: Any) -> None:
|
|
12
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
13
|
+
payload = {
|
|
14
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
15
|
+
"event": event,
|
|
16
|
+
**fields,
|
|
17
|
+
}
|
|
18
|
+
with path.open("a", encoding="utf-8") as handle:
|
|
19
|
+
handle.write(json.dumps(payload, sort_keys=True) + "\n")
|
|
20
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""ocmemog-native memory namespace with native surfaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
|
|
7
|
+
# Native-first memory surfaces (core path has no package-level wrapper bootstrapping).
|
|
8
|
+
from . import (
|
|
9
|
+
api,
|
|
10
|
+
candidate,
|
|
11
|
+
memory_consolidation,
|
|
12
|
+
distill,
|
|
13
|
+
embedding_engine,
|
|
14
|
+
health,
|
|
15
|
+
integrity,
|
|
16
|
+
memory_taxonomy,
|
|
17
|
+
memory_links,
|
|
18
|
+
provenance,
|
|
19
|
+
promote,
|
|
20
|
+
retrieval,
|
|
21
|
+
store,
|
|
22
|
+
unresolved_state,
|
|
23
|
+
vector_index,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
_NATIVE_MEMORY_SURFACES = (
|
|
27
|
+
"api",
|
|
28
|
+
"candidate",
|
|
29
|
+
"distill",
|
|
30
|
+
"embedding_engine",
|
|
31
|
+
"health",
|
|
32
|
+
"integrity",
|
|
33
|
+
"memory_consolidation",
|
|
34
|
+
"memory_taxonomy",
|
|
35
|
+
"memory_links",
|
|
36
|
+
"provenance",
|
|
37
|
+
"promote",
|
|
38
|
+
"retrieval",
|
|
39
|
+
"store",
|
|
40
|
+
"unresolved_state",
|
|
41
|
+
"vector_index",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
_MEMORY_SURFACES = (
|
|
45
|
+
"conversation_state",
|
|
46
|
+
"memory_synthesis",
|
|
47
|
+
"pondering_engine",
|
|
48
|
+
"reinforcement",
|
|
49
|
+
"semantic_search",
|
|
50
|
+
"memory_salience",
|
|
51
|
+
"freshness",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def __getattr__(name: str):
|
|
56
|
+
if name in _NATIVE_MEMORY_SURFACES:
|
|
57
|
+
return globals()[name]
|
|
58
|
+
if name in _MEMORY_SURFACES:
|
|
59
|
+
module = importlib.import_module(f"{__name__}.{name}")
|
|
60
|
+
globals()[name] = module
|
|
61
|
+
return module
|
|
62
|
+
raise AttributeError(name)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
"api",
|
|
67
|
+
"candidate",
|
|
68
|
+
"conversation_state",
|
|
69
|
+
"distill",
|
|
70
|
+
"embedding_engine",
|
|
71
|
+
"health",
|
|
72
|
+
"integrity",
|
|
73
|
+
"memory_links",
|
|
74
|
+
"memory_consolidation",
|
|
75
|
+
"memory_taxonomy",
|
|
76
|
+
"memory_synthesis",
|
|
77
|
+
"promote",
|
|
78
|
+
"provenance",
|
|
79
|
+
"pondering_engine",
|
|
80
|
+
"freshness",
|
|
81
|
+
"memory_salience",
|
|
82
|
+
"reinforcement",
|
|
83
|
+
"retrieval",
|
|
84
|
+
"semantic_search",
|
|
85
|
+
"store",
|
|
86
|
+
"unresolved_state",
|
|
87
|
+
"vector_index",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
# No legacy-shimmed memory surfaces remain in this namespace.
|
|
91
|
+
__legacy_memory_surfaces__ = ()
|