dsh-biocore 0.1.0 → 0.1.2
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/README.md +2 -2
- package/lib/py/bridge.py +15 -7
- package/lib/py-bridge.js +4 -3
- package/package.json +3 -2
- package/python/biocore_memory/__init__.py +21 -0
- package/python/biocore_memory/adapters/__init__.py +2 -0
- package/python/biocore_memory/adapters/astrbot.py +24 -0
- package/python/biocore_memory/adapters/dsh_cli.py +335 -0
- package/python/biocore_memory/co_recall.py +324 -0
- package/python/biocore_memory/consolidate/__init__.py +11 -0
- package/python/biocore_memory/consolidate/gated.py +104 -0
- package/python/biocore_memory/consolidate/neighbors.py +68 -0
- package/python/biocore_memory/consolidate/reflection.py +45 -0
- package/python/biocore_memory/consolidate/tree.py +95 -0
- package/python/biocore_memory/consolidate/writeback.py +181 -0
- package/python/biocore_memory/core/__init__.py +4 -0
- package/python/biocore_memory/core/config.py +127 -0
- package/python/biocore_memory/core/permissions.py +69 -0
- package/python/biocore_memory/core/types.py +116 -0
- package/python/biocore_memory/core/util.py +26 -0
- package/python/biocore_memory/embedding_local.py +447 -0
- package/python/biocore_memory/embedding_mode.py +156 -0
- package/python/biocore_memory/engine.py +2476 -0
- package/python/biocore_memory/engine_helpers.py +266 -0
- package/python/biocore_memory/event/__init__.py +3 -0
- package/python/biocore_memory/event/_types.py +16 -0
- package/python/biocore_memory/event/consistency.py +117 -0
- package/python/biocore_memory/event/extract.py +48 -0
- package/python/biocore_memory/event/media.py +111 -0
- package/python/biocore_memory/event/notes.py +85 -0
- package/python/biocore_memory/event/notes_link.py +87 -0
- package/python/biocore_memory/event/vector.py +69 -0
- package/python/biocore_memory/evict/__init__.py +3 -0
- package/python/biocore_memory/evict/adaptive.py +73 -0
- package/python/biocore_memory/evict/consequence.py +58 -0
- package/python/biocore_memory/evict/heat.py +54 -0
- package/python/biocore_memory/evict/inhibition.py +105 -0
- package/python/biocore_memory/evict/policy.py +188 -0
- package/python/biocore_memory/evict/score.py +23 -0
- package/python/biocore_memory/identity/__init__.py +6 -0
- package/python/biocore_memory/identity/api.py +99 -0
- package/python/biocore_memory/identity/history.py +9 -0
- package/python/biocore_memory/identity/ledger.py +191 -0
- package/python/biocore_memory/identity/narrative.py +358 -0
- package/python/biocore_memory/identity/snapshot.py +13 -0
- package/python/biocore_memory/identity/writeback.py +61 -0
- package/python/biocore_memory/index/__init__.py +3 -0
- package/python/biocore_memory/index/cue_index.py +80 -0
- package/python/biocore_memory/index/entities.py +85 -0
- package/python/biocore_memory/index/entity_boost.py +104 -0
- package/python/biocore_memory/index/graph.py +172 -0
- package/python/biocore_memory/recall/__init__.py +5 -0
- package/python/biocore_memory/recall/api.py +65 -0
- package/python/biocore_memory/recall/bm25.py +118 -0
- package/python/biocore_memory/recall/channels.py +98 -0
- package/python/biocore_memory/recall/complexity.py +62 -0
- package/python/biocore_memory/recall/constraints.py +75 -0
- package/python/biocore_memory/recall/embedding.py +745 -0
- package/python/biocore_memory/recall/expand.py +26 -0
- package/python/biocore_memory/recall/filters.py +40 -0
- package/python/biocore_memory/recall/fusion.py +18 -0
- package/python/biocore_memory/recall/mmr.py +70 -0
- package/python/biocore_memory/recall/ppr.py +85 -0
- package/python/biocore_memory/recall/retrieval_practice.py +102 -0
- package/python/biocore_memory/relay/__init__.py +2 -0
- package/python/biocore_memory/relay/card.py +51 -0
- package/python/biocore_memory/slots/__init__.py +18 -0
- package/python/biocore_memory/slots/admission.py +64 -0
- package/python/biocore_memory/slots/boundary.py +128 -0
- package/python/biocore_memory/slots/evict_bridge.py +106 -0
- package/python/biocore_memory/slots/registry.py +135 -0
- package/python/biocore_memory/slots/switch.py +42 -0
- package/python/biocore_memory/store/__init__.py +4 -0
- package/python/biocore_memory/store/audit.py +74 -0
- package/python/biocore_memory/store/compress.py +81 -0
- package/python/biocore_memory/store/dedupe.py +178 -0
- package/python/biocore_memory/store/json_store.py +154 -0
- package/python/biocore_memory/store/media.py +145 -0
- package/python/biocore_memory/store/provenance.py +73 -0
- package/python/biocore_memory/store/rate_limit.py +93 -0
- package/python/biocore_memory/store/security.py +107 -0
- package/python/biocore_memory/store/source_consistency.py +123 -0
- package/python/biocore_memory/store/tiering.py +61 -0
- package/python/biocore_memory/store/tiering_exec.py +50 -0
- package/python/biocore_memory/store/tombstone.py +23 -0
- package/python/biocore_memory/store/versions.py +69 -0
- package/python/biocore_memory/thread/__init__.py +2 -0
- package/python/biocore_memory/thread/card.py +58 -0
- package/python/biocore_model/__init__.py +37 -0
- package/python/biocore_model/affective_labor.py +162 -0
- package/python/biocore_model/appraisal.py +257 -0
- package/python/biocore_model/appraisal_action.py +78 -0
- package/python/biocore_model/archive_queue.py +60 -0
- package/python/biocore_model/attachment_behavior.py +130 -0
- package/python/biocore_model/attachment_regulation.py +75 -0
- package/python/biocore_model/attachment_state.py +41 -0
- package/python/biocore_model/attribution.py +73 -0
- package/python/biocore_model/attributor.py +162 -0
- package/python/biocore_model/autonomic.py +91 -0
- package/python/biocore_model/autonomy_affect.py +51 -0
- package/python/biocore_model/autonomy_check.py +67 -0
- package/python/biocore_model/autonomy_loop.py +122 -0
- package/python/biocore_model/autonomy_mode.py +114 -0
- package/python/biocore_model/autonomy_model.py +733 -0
- package/python/biocore_model/autonomy_params.py +131 -0
- package/python/biocore_model/autonomy_reason.py +73 -0
- package/python/biocore_model/autonomy_tick.py +227 -0
- package/python/biocore_model/bio_appraisal.py +49 -0
- package/python/biocore_model/bio_autonomy_state.py +171 -0
- package/python/biocore_model/bio_config.py +221 -0
- package/python/biocore_model/bio_const.py +128 -0
- package/python/biocore_model/bio_daily.py +267 -0
- package/python/biocore_model/bio_emotion.py +538 -0
- package/python/biocore_model/bio_emotion2.py +214 -0
- package/python/biocore_model/bio_goal.py +220 -0
- package/python/biocore_model/bio_identity.py +82 -0
- package/python/biocore_model/bio_noise.py +119 -0
- package/python/biocore_model/bio_physiology2.py +114 -0
- package/python/biocore_model/bio_snapshot.py +324 -0
- package/python/biocore_model/bio_state.py +272 -0
- package/python/biocore_model/bio_tick.py +310 -0
- package/python/biocore_model/bio_util.py +82 -0
- package/python/biocore_model/blocks.py +264 -0
- package/python/biocore_model/boundary.py +251 -0
- package/python/biocore_model/boundary_gate.py +188 -0
- package/python/biocore_model/commitment_extractor.py +517 -0
- package/python/biocore_model/compassion_fatigue.py +84 -0
- package/python/biocore_model/connection_bids.py +120 -0
- package/python/biocore_model/core_bridge.py +177 -0
- package/python/biocore_model/core_client.py +127 -0
- package/python/biocore_model/core_commands.py +112 -0
- package/python/biocore_model/core_mode.py +104 -0
- package/python/biocore_model/core_mode_ui.mjs +104 -0
- package/python/biocore_model/criticality.py +81 -0
- package/python/biocore_model/defense_mechanisms.py +272 -0
- package/python/biocore_model/delta_budget.py +99 -0
- package/python/biocore_model/diag_trace.py +274 -0
- package/python/biocore_model/dissonance.py +134 -0
- package/python/biocore_model/emotion_geometry.py +108 -0
- package/python/biocore_model/emotion_probe.py +209 -0
- package/python/biocore_model/entity_graph.py +931 -0
- package/python/biocore_model/entity_resolve.py +260 -0
- package/python/biocore_model/envy.py +193 -0
- package/python/biocore_model/exit_or_silence.py +230 -0
- package/python/biocore_model/expr_guard.py +24 -0
- package/python/biocore_model/expr_intent.py +77 -0
- package/python/biocore_model/expr_state.py +204 -0
- package/python/biocore_model/expr_template.py +105 -0
- package/python/biocore_model/expr_tone.py +81 -0
- package/python/biocore_model/extreme_scenarios.py +205 -0
- package/python/biocore_model/feedback_flow.py +227 -0
- package/python/biocore_model/feedback_guard.py +129 -0
- package/python/biocore_model/feedback_llm.py +163 -0
- package/python/biocore_model/feedback_memory.py +96 -0
- package/python/biocore_model/feedback_user.py +93 -0
- package/python/biocore_model/frustration_curve.py +110 -0
- package/python/biocore_model/granularity.py +283 -0
- package/python/biocore_model/graph_extract.py +248 -0
- package/python/biocore_model/graph_recall.py +221 -0
- package/python/biocore_model/intent_render.py +91 -0
- package/python/biocore_model/intero_core.py +235 -0
- package/python/biocore_model/intero_style.py +116 -0
- package/python/biocore_model/layered_memory.py +297 -0
- package/python/biocore_model/learning_probe.py +120 -0
- package/python/biocore_model/memory_dual.py +224 -0
- package/python/biocore_model/memory_forgetting.py +125 -0
- package/python/biocore_model/memory_store.py +1399 -0
- package/python/biocore_model/metacog.py +153 -0
- package/python/biocore_model/migrate.py +733 -0
- package/python/biocore_model/migrate_store.py +337 -0
- package/python/biocore_model/mind_wander.py +111 -0
- package/python/biocore_model/mood.py +48 -0
- package/python/biocore_model/narrative.py +172 -0
- package/python/biocore_model/needs_esteem.py +116 -0
- package/python/biocore_model/needs_events.py +137 -0
- package/python/biocore_model/needs_sleep.py +72 -0
- package/python/biocore_model/noise_bank.py +95 -0
- package/python/biocore_model/persona_flow.mjs +25 -0
- package/python/biocore_model/pipeline.py +240 -0
- package/python/biocore_model/pipeline_api.py +200 -0
- package/python/biocore_model/plugin_config.py +83 -0
- package/python/biocore_model/plugin_routes.py +1435 -0
- package/python/biocore_model/policy_learning.py +132 -0
- package/python/biocore_model/possible_selves.py +110 -0
- package/python/biocore_model/predictor.py +178 -0
- package/python/biocore_model/preset.py +205 -0
- package/python/biocore_model/proactive_context.py +153 -0
- package/python/biocore_model/prompt_rules.py +90 -0
- package/python/biocore_model/prospective.py +183 -0
- package/python/biocore_model/provider_fallback.py +97 -0
- package/python/biocore_model/reconstruction.py +132 -0
- package/python/biocore_model/regulation_loop.py +179 -0
- package/python/biocore_model/regulation_strategies.py +294 -0
- package/python/biocore_model/relation_stage.py +130 -0
- package/python/biocore_model/relationship_stage.py +95 -0
- package/python/biocore_model/render.py +901 -0
- package/python/biocore_model/render_guard.mjs +34 -0
- package/python/biocore_model/repair_after_boundary.py +350 -0
- package/python/biocore_model/reply_limit.py +183 -0
- package/python/biocore_model/reply_split.py +203 -0
- package/python/biocore_model/rollback.py +143 -0
- package/python/biocore_model/runtime.py +479 -0
- package/python/biocore_model/sanitize.py +200 -0
- package/python/biocore_model/scheduler.py +123 -0
- package/python/biocore_model/schema.py +120 -0
- package/python/biocore_model/secrecy.py +145 -0
- package/python/biocore_model/self_esteem.py +50 -0
- package/python/biocore_model/self_intent.py +437 -0
- package/python/biocore_model/self_interest.py +263 -0
- package/python/biocore_model/self_verification.py +178 -0
- package/python/biocore_model/services.py +78 -0
- package/python/biocore_model/situation.py +254 -0
- package/python/biocore_model/sleep_circadian.py +116 -0
- package/python/biocore_model/social_pain.py +69 -0
- package/python/biocore_model/social_repair.py +111 -0
- package/python/biocore_model/social_theory.py +131 -0
- package/python/biocore_model/source_monitoring.py +292 -0
- package/python/biocore_model/stages.py +487 -0
- package/python/biocore_model/state_tag.py +231 -0
- package/python/biocore_model/switch_groups.mjs +201 -0
- package/python/biocore_model/tabs_state.mjs +37 -0
- package/python/biocore_model/template_expr.py +252 -0
- package/python/biocore_model/tools/caps_check.mjs +23 -0
- package/python/biocore_model/tools/init_symbols_check.mjs +11 -0
- package/python/biocore_model/tools/missing_ids.mjs +16 -0
- package/python/biocore_model/tools/scope_check.mjs +19 -0
- package/python/biocore_model/topic_control.py +268 -0
- package/python/biocore_model/turn_memory.py +152 -0
- package/python/biocore_model/ui_compat.mjs +48 -0
- package/python/biocore_model/ui_settings.py +266 -0
- package/python/biocore_model/ui_settings.py.bak_authority +108 -0
- package/python/biocore_model/value_order.py +186 -0
- package/python/biocore_model/view_mode.mjs +8 -0
- package/python/biocore_model/voice_bank.py +177 -0
- package/python/biocore_model/waiting_sense.py +229 -0
- package/python/biocore_model/x_directives.py +142 -0
- package/python/biocore_runtime/__init__.py +7 -0
- package/python/biocore_runtime/pipeline.py +1915 -0
- package/python/dsh_adapter/README.md +96 -0
- package/python/dsh_adapter/adapter.py +1077 -0
- package/python/dsh_adapter/cli.py +107 -0
- package/python/dsh_adapter/data/demo_state.json +164 -0
- package/python/dsh_adapter/data/verify_state.json +164 -0
- package/python/dsh_adapter/tests/__init__.py +1 -0
- package/python/dsh_adapter/tests/test_adapter.py +494 -0
- package/ui/panel-layout.js +3 -3
- package/ui/panel.css +53 -6
- package/ui/panel.html +33 -32
- package/ui/panel.js +9 -1
- package/lib/py/__pycache__/bridge.cpython-310.pyc +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Write-back bridge for consolidation suggestions (default OFF, idempotent).
|
|
2
|
+
Pure/local: operates on an in-memory MemoryStore-like object (entries/add/update).
|
|
3
|
+
It never saves to disk by itself; the caller decides when to call store.save().
|
|
4
|
+
All failures degrade to skipped entries instead of raising.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import hashlib
|
|
8
|
+
import json
|
|
9
|
+
WRITEBACK_ENABLED = False
|
|
10
|
+
MAX_RELATED = 8
|
|
11
|
+
REFLECTION_SOURCE = "consolidate"
|
|
12
|
+
TREE_SOURCE = "ti_mem"
|
|
13
|
+
def _digest(text):
|
|
14
|
+
return hashlib.sha256(str(text or "").encode("utf-8")).hexdigest()[:12]
|
|
15
|
+
def _entries(store):
|
|
16
|
+
try:
|
|
17
|
+
rows = getattr(store, "entries", None)
|
|
18
|
+
if isinstance(rows, list):
|
|
19
|
+
return rows
|
|
20
|
+
if isinstance(store, dict):
|
|
21
|
+
rows = store.get("entries")
|
|
22
|
+
return rows if isinstance(rows, list) else []
|
|
23
|
+
return list(store or [])
|
|
24
|
+
except Exception:
|
|
25
|
+
return []
|
|
26
|
+
def _find(store, entry_id):
|
|
27
|
+
try:
|
|
28
|
+
fn = getattr(store, "find", None)
|
|
29
|
+
if callable(fn):
|
|
30
|
+
row = fn(entry_id)
|
|
31
|
+
if isinstance(row, dict):
|
|
32
|
+
return row
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
for entry in _entries(store):
|
|
36
|
+
if isinstance(entry, dict) and str(entry.get("id") or "") == str(entry_id):
|
|
37
|
+
return entry
|
|
38
|
+
return None
|
|
39
|
+
def _add(store, entry):
|
|
40
|
+
try:
|
|
41
|
+
fn = getattr(store, "add", None)
|
|
42
|
+
if callable(fn):
|
|
43
|
+
return fn(entry)
|
|
44
|
+
if isinstance(store, dict):
|
|
45
|
+
store.setdefault("entries", []).append(entry)
|
|
46
|
+
return entry
|
|
47
|
+
store.append(entry)
|
|
48
|
+
return entry
|
|
49
|
+
except Exception:
|
|
50
|
+
return None
|
|
51
|
+
def _update(store, entry_id, **fields):
|
|
52
|
+
try:
|
|
53
|
+
fn = getattr(store, "update", None)
|
|
54
|
+
if callable(fn):
|
|
55
|
+
result = fn(entry_id, **fields)
|
|
56
|
+
if result is not None:
|
|
57
|
+
return result
|
|
58
|
+
except Exception:
|
|
59
|
+
pass
|
|
60
|
+
entry = _find(store, entry_id)
|
|
61
|
+
if isinstance(entry, dict):
|
|
62
|
+
entry.update(fields)
|
|
63
|
+
return entry
|
|
64
|
+
return None
|
|
65
|
+
def _source_ids(proposal):
|
|
66
|
+
raw = proposal.get("source_ids") if isinstance(proposal, dict) else []
|
|
67
|
+
return sorted({str(x) for x in (raw or []) if str(x)})
|
|
68
|
+
def _scope(entry):
|
|
69
|
+
fields = ("session", "scope", "user_id", "agent_id", "app_id", "run_id", "thread")
|
|
70
|
+
return {k: entry.get(k) for k in fields if entry.get(k) is not None}
|
|
71
|
+
def _importance(store, ids, default=0.5):
|
|
72
|
+
values = []
|
|
73
|
+
for sid in ids:
|
|
74
|
+
row = _find(store, sid)
|
|
75
|
+
if isinstance(row, dict):
|
|
76
|
+
try:
|
|
77
|
+
values.append(float(row.get("importance", 0.5) or 0.5))
|
|
78
|
+
except Exception:
|
|
79
|
+
continue
|
|
80
|
+
return round(sum(values) / len(values), 4) if values else default
|
|
81
|
+
def apply_reflections(store, proposals):
|
|
82
|
+
"""Write reflection proposals as system entries; idempotent by source_ids."""
|
|
83
|
+
result = {"written": [], "skipped": []}
|
|
84
|
+
if not WRITEBACK_ENABLED:
|
|
85
|
+
result["skipped"].append({"reason": "disabled"})
|
|
86
|
+
return result
|
|
87
|
+
for idx, proposal in enumerate(proposals or []):
|
|
88
|
+
try:
|
|
89
|
+
text = str((proposal or {}).get("text") or "").strip() if isinstance(proposal, dict) else ""
|
|
90
|
+
ids = _source_ids(proposal)
|
|
91
|
+
if len(text) < 2 or not ids:
|
|
92
|
+
result["skipped"].append({"index": idx, "reason": "invalid"})
|
|
93
|
+
continue
|
|
94
|
+
duplicate = any(
|
|
95
|
+
str(e.get("source") or "") == REFLECTION_SOURCE
|
|
96
|
+
and sorted(str(x) for x in ((e.get("metadata") or {}).get("source_ids") or []))
|
|
97
|
+
== ids
|
|
98
|
+
for e in _entries(store) if isinstance(e, dict))
|
|
99
|
+
if duplicate:
|
|
100
|
+
result["skipped"].append({"index": idx, "reason": "duplicate"})
|
|
101
|
+
continue
|
|
102
|
+
base = {"id": "consolidate_" + _digest(json.dumps([text, ids]))}
|
|
103
|
+
base.update(_scope(_find(store, ids[0]) or {}))
|
|
104
|
+
base.update({"text": text, "role": "system", "source": REFLECTION_SOURCE,
|
|
105
|
+
"importance": _importance(store, ids),
|
|
106
|
+
"metadata": {"source_ids": ids, "origin": "reflection"}})
|
|
107
|
+
added = _add(store, base)
|
|
108
|
+
if isinstance(added, dict) and added.get("id"):
|
|
109
|
+
result["written"].append(str(added.get("id")))
|
|
110
|
+
else:
|
|
111
|
+
result["skipped"].append({"index": idx, "reason": "add_failed"})
|
|
112
|
+
except Exception:
|
|
113
|
+
result["skipped"].append({"index": idx, "reason": "error"})
|
|
114
|
+
return result
|
|
115
|
+
def _suggestions(suggestions):
|
|
116
|
+
if isinstance(suggestions, dict):
|
|
117
|
+
return [suggestions]
|
|
118
|
+
return [s for s in (suggestions or []) if isinstance(s, dict)]
|
|
119
|
+
def apply_neighbors(store, suggestions):
|
|
120
|
+
"""Append strengthen() link_ids to related_ids (dedupe, total <=8)."""
|
|
121
|
+
result = {"linked": 0}
|
|
122
|
+
if not WRITEBACK_ENABLED:
|
|
123
|
+
return result
|
|
124
|
+
for suggestion in _suggestions(suggestions):
|
|
125
|
+
try:
|
|
126
|
+
eid = str(suggestion.get("entry_id") or "")
|
|
127
|
+
entry = _find(store, eid)
|
|
128
|
+
if not isinstance(entry, dict):
|
|
129
|
+
continue
|
|
130
|
+
current = entry.get("related_ids")
|
|
131
|
+
current = [str(x) for x in current if str(x)] if isinstance(current, list) else []
|
|
132
|
+
links = [str(x) for x in (suggestion.get("link_ids") or []) if str(x) and str(x) != eid]
|
|
133
|
+
merged = []
|
|
134
|
+
for value in current + links:
|
|
135
|
+
if value not in merged:
|
|
136
|
+
merged.append(value)
|
|
137
|
+
merged = merged[:MAX_RELATED]
|
|
138
|
+
if merged == current:
|
|
139
|
+
continue
|
|
140
|
+
if _update(store, eid, related_ids=merged):
|
|
141
|
+
result["linked"] += 1
|
|
142
|
+
except Exception:
|
|
143
|
+
continue
|
|
144
|
+
return result
|
|
145
|
+
def _day_of(items):
|
|
146
|
+
days = [str((x or {}).get("day") or (x or {}).get("key") or "") for x in (items or [])]
|
|
147
|
+
for value in days:
|
|
148
|
+
if len(value) >= 10 and value[:4].isdigit():
|
|
149
|
+
return value[:10]
|
|
150
|
+
return days[0][:10] if days and days[0] else ""
|
|
151
|
+
def write_tree(store, summary):
|
|
152
|
+
"""Write one tree summary entry (level/day); idempotent by source+level+day."""
|
|
153
|
+
level = int((summary or {}).get("level") or 1) if isinstance(summary, dict) else 1
|
|
154
|
+
result = {"written": 0, "level": level}
|
|
155
|
+
if not WRITEBACK_ENABLED or not isinstance(summary, dict):
|
|
156
|
+
return result
|
|
157
|
+
try:
|
|
158
|
+
items = list(summary.get("items") or [])
|
|
159
|
+
if not summary.get("generated") or not items:
|
|
160
|
+
return result
|
|
161
|
+
day = _day_of(items)
|
|
162
|
+
duplicate = any(
|
|
163
|
+
str(e.get("source") or "") == TREE_SOURCE
|
|
164
|
+
and int((e.get("metadata") or {}).get("level") or 0) == level
|
|
165
|
+
and str((e.get("metadata") or {}).get("day") or "") == day
|
|
166
|
+
for e in _entries(store) if isinstance(e, dict))
|
|
167
|
+
if duplicate:
|
|
168
|
+
return result
|
|
169
|
+
text = "时间树 L%d %s:%s" % (
|
|
170
|
+
level, day, json.dumps(items[:10], ensure_ascii=False, sort_keys=True)[:1000])
|
|
171
|
+
entry = {"id": TREE_SOURCE + "_" + _digest("%s|%s|%s" % (level, day, text)),
|
|
172
|
+
"text": text[:1200], "role": "system", "source": TREE_SOURCE,
|
|
173
|
+
"importance": 0.5,
|
|
174
|
+
"metadata": {"level": level, "day": day,
|
|
175
|
+
"source_ids": [str(x.get("key") or "") for x in items[:8]]}}
|
|
176
|
+
added = _add(store, entry)
|
|
177
|
+
if isinstance(added, dict):
|
|
178
|
+
result["written"] = 1
|
|
179
|
+
return result
|
|
180
|
+
except Exception:
|
|
181
|
+
return result
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""All memory-layer thresholds/defaults in one place."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
SCHEMA_VERSION = 1
|
|
6
|
+
MAX_VERSIONS = 8
|
|
7
|
+
TOMBSTONE_LIMIT = 100
|
|
8
|
+
DEFAULT_BUDGET = 3
|
|
9
|
+
MAX_BUDGET = 5
|
|
10
|
+
|
|
11
|
+
# recall fusion / tiers (M2)
|
|
12
|
+
CUE_WEIGHT = 0.50
|
|
13
|
+
SEM_WEIGHT = 0.30
|
|
14
|
+
IMPORTANCE_WEIGHT = 0.10
|
|
15
|
+
TIME_BONUS = 0.10
|
|
16
|
+
HIGH_TIER = 0.55
|
|
17
|
+
MID_TIER = 0.35
|
|
18
|
+
|
|
19
|
+
# source credibility gate (separate from importance)
|
|
20
|
+
SOURCE_MIN_CONFIDENCE = 0.55
|
|
21
|
+
SOURCE_CONFIDENCE_DEFAULT = 0.70
|
|
22
|
+
|
|
23
|
+
# memory value formula (deterministic; trust is NOT part of value)
|
|
24
|
+
VALUE_BASE = 0.20
|
|
25
|
+
VALUE_W_INTENSITY = 0.30
|
|
26
|
+
VALUE_W_VALENCE = 0.20
|
|
27
|
+
VALUE_W_SELF = 0.15
|
|
28
|
+
VALUE_W_MAINLINE = 0.15
|
|
29
|
+
VALUE_W_ROUTINE = 0.20
|
|
30
|
+
|
|
31
|
+
# eviction weights (read-only score)
|
|
32
|
+
EVICT_W_SALIENCE = 0.30
|
|
33
|
+
EVICT_W_POSITION = 0.20
|
|
34
|
+
EVICT_W_REDUNDANCY = 0.20
|
|
35
|
+
EVICT_W_CONFIDENCE = 0.15
|
|
36
|
+
EVICT_W_STALENESS = 0.15
|
|
37
|
+
|
|
38
|
+
# identity write-back gate
|
|
39
|
+
SAME_DIRECTION_MIN = 3
|
|
40
|
+
MAX_STEP = 0.02
|
|
41
|
+
MAJOR_EVENT_STEP = 0.02
|
|
42
|
+
|
|
43
|
+
# thread card
|
|
44
|
+
THREAD_TTL = 21600.0
|
|
45
|
+
THREAD_LIMIT = 3
|
|
46
|
+
|
|
47
|
+
# memory-point gate (OR): value >= MIN_VALUE or importance >= MIN_IMPORTANCE or self >= MIN_SELF
|
|
48
|
+
# (event centrality / emotional intensity; self-reference effect; engineering thresholds)
|
|
49
|
+
MEMORY_POINT_MIN_VALUE = 0.60
|
|
50
|
+
MEMORY_POINT_MIN_SELF = 0.45
|
|
51
|
+
MEMORY_POINT_MIN_IMPORTANCE = 0.75
|
|
52
|
+
|
|
53
|
+
# engine wiring knobs (all read by MemoryEngine only)
|
|
54
|
+
RATE_LIMIT_PER_MIN = 30
|
|
55
|
+
COMPRESS_MAX_CHARS = 200
|
|
56
|
+
AUDIT_TAIL = 50
|
|
57
|
+
COOC_MAX_ENTRIES = 400
|
|
58
|
+
# after-turn auto consolidation: every N successful remember() calls
|
|
59
|
+
CONSOLIDATE_AUTO_TURNS = 10
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Per-mechanism flags: safety/read-path filters default ON; every optional
|
|
63
|
+
# mechanism defaults OFF and must keep the old code path when disabled.
|
|
64
|
+
# A flags.json in the data directory overrides these booleans at engine start
|
|
65
|
+
# (and MemoryEngine.set_flags(..., persist=True) rewrites it).
|
|
66
|
+
FLAGS = {
|
|
67
|
+
# safety / read-path (default ON)
|
|
68
|
+
# GA-03: keep engine default ON. DSH private mode disables it per-store via
|
|
69
|
+
# <dsh store>/flags.json; the shared engine must not be relaxed globally.
|
|
70
|
+
"security": True, # store/security.should_reject before write
|
|
71
|
+
"provenance": True, # provenance hash tag + chained tombstones
|
|
72
|
+
"source_filter": True, # low-confidence candidates are counted/dropped
|
|
73
|
+
"temporal_filter": True, # honour valid_at/invalid_at on recall
|
|
74
|
+
"dedupe": True, # remember-side L0-L3 dedupe
|
|
75
|
+
"relay": True, # relay card
|
|
76
|
+
"thread": True, # thread card / TTL expiry
|
|
77
|
+
"embedding": True, # optional embedding provider
|
|
78
|
+
# attention / model features (default OFF)
|
|
79
|
+
"slots": False,
|
|
80
|
+
"boundary": False, # slots/boundary.py event-boundary switching
|
|
81
|
+
"three_channel": False,
|
|
82
|
+
"eviction_readonly": False,
|
|
83
|
+
"identity": False,
|
|
84
|
+
"identity_writeback": False,
|
|
85
|
+
"due_hints": False,
|
|
86
|
+
# recall extensions (default OFF)
|
|
87
|
+
"bm25": False,
|
|
88
|
+
"bm25_multi": False,
|
|
89
|
+
"ppr": False,
|
|
90
|
+
"mmr": False,
|
|
91
|
+
"complexity": False,
|
|
92
|
+
"constraints": False,
|
|
93
|
+
"tiering": False,
|
|
94
|
+
"entity_boost": False,
|
|
95
|
+
"inhibition": False,
|
|
96
|
+
"retrieval_practice": False,
|
|
97
|
+
"compress": False, # output-only summary, never mutates the store
|
|
98
|
+
"source_consistency": False, # source-rank marking/downweight on recall
|
|
99
|
+
# eviction extension modules (default OFF)
|
|
100
|
+
"heat": False,
|
|
101
|
+
"consequence": False,
|
|
102
|
+
"tiering_exec": False,
|
|
103
|
+
"apply_evictions": False, # tick() may really soft-delete archived entries
|
|
104
|
+
"deletion_audit": False, # derived-data coverage audit after forget()
|
|
105
|
+
"adaptive_evict": False, # adaptive_score replaces the tick eviction score
|
|
106
|
+
|
|
107
|
+
# write-path extensions (default OFF)
|
|
108
|
+
"consistency": False, # old/new fact conflict annotation on remember
|
|
109
|
+
"rate_limit": False,
|
|
110
|
+
"audit": False,
|
|
111
|
+
"permissions": False,
|
|
112
|
+
"notes": False,
|
|
113
|
+
"media": False,
|
|
114
|
+
"notes_link": False,
|
|
115
|
+
"ledger": False,
|
|
116
|
+
# index / graph
|
|
117
|
+
"graph": False,
|
|
118
|
+
"cue_writeback": True, # store cue_index fields on each new entry (index-only, no recall change)
|
|
119
|
+
# consolidation modules (default OFF)
|
|
120
|
+
"consolidate_auto": False, # after-turn auto consolidate every CONSOLIDATE_AUTO_TURNS remembers
|
|
121
|
+
"consolidate_gated": False,
|
|
122
|
+
"consolidate_tree": False,
|
|
123
|
+
"consolidate_reflection": False,
|
|
124
|
+
"consolidate_neighbors": False,
|
|
125
|
+
"consolidate_writeback": False,
|
|
126
|
+
"narrative": False, # identity narrative update at consolidate end
|
|
127
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Scope-based read/write permissions (default OFF; pure, no astrbot)."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
enabled = False
|
|
6
|
+
ENABLED = False
|
|
7
|
+
ADMIN_ROLES = ("admin", "owner", "system", "superuser", "root")
|
|
8
|
+
READONLY_ROLES = ("guest", "readonly", "viewer")
|
|
9
|
+
PUBLIC_SCOPES = ("*", "public", "all", "shared")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _principal(principal):
|
|
13
|
+
value = principal if isinstance(principal, dict) else {}
|
|
14
|
+
role = str(value.get("role") or "user").strip().lower()
|
|
15
|
+
identifier = str(value.get("id") or value.get("user_id") or "").strip()
|
|
16
|
+
scopes = value.get("scopes")
|
|
17
|
+
if not isinstance(scopes, list):
|
|
18
|
+
scopes = [value.get("scope")] if value.get("scope") is not None else []
|
|
19
|
+
return role, identifier, {str(s).strip() for s in scopes if str(s).strip()}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _scopes(entry):
|
|
23
|
+
if not isinstance(entry, dict):
|
|
24
|
+
return set()
|
|
25
|
+
raw = entry.get("scope")
|
|
26
|
+
if raw is None:
|
|
27
|
+
raw = entry.get("scopes")
|
|
28
|
+
if isinstance(raw, str):
|
|
29
|
+
raw = [raw]
|
|
30
|
+
if not isinstance(raw, (list, tuple, set)):
|
|
31
|
+
return set()
|
|
32
|
+
return {str(value).strip() for value in raw if str(value).strip()}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _can_access(entry, principal):
|
|
36
|
+
if not enabled:
|
|
37
|
+
return True
|
|
38
|
+
role, identifier, principal_scopes = _principal(principal)
|
|
39
|
+
if role in ADMIN_ROLES:
|
|
40
|
+
return True
|
|
41
|
+
scopes = _scopes(entry)
|
|
42
|
+
if not scopes:
|
|
43
|
+
return True
|
|
44
|
+
if scopes & set(PUBLIC_SCOPES):
|
|
45
|
+
return True
|
|
46
|
+
if identifier and (identifier in scopes or ("user:" + identifier) in scopes):
|
|
47
|
+
return True
|
|
48
|
+
return bool(principal_scopes & scopes)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def can_read(entry, principal=None):
|
|
52
|
+
"""True when principal may read entry (disabled -> always True)."""
|
|
53
|
+
try:
|
|
54
|
+
return _can_access(entry, principal)
|
|
55
|
+
except Exception:
|
|
56
|
+
return not enabled
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def can_write(entry, principal=None):
|
|
60
|
+
"""True when principal may write entry; readonly roles are denied when on."""
|
|
61
|
+
try:
|
|
62
|
+
if not enabled:
|
|
63
|
+
return True
|
|
64
|
+
role, _identifier, _scopes_set = _principal(principal)
|
|
65
|
+
if role in READONLY_ROLES:
|
|
66
|
+
return False
|
|
67
|
+
return _can_access(entry, principal)
|
|
68
|
+
except Exception:
|
|
69
|
+
return not enabled
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Host-neutral data structures for the memory layer.
|
|
3
|
+
|
|
4
|
+
Interface summary:
|
|
5
|
+
MemoryEntry(id, text, role, session, ts, vector, importance, pin, metadata, scope)
|
|
6
|
+
Cue(entities, place, time, event_name, id)
|
|
7
|
+
EventVector(intensity, self_relevance, schema_conflict, relation_weight,
|
|
8
|
+
novelty, unresolved, repetition)
|
|
9
|
+
ThreadCard(id, text, ts); RelayCard(identity, relationship, open_loops,
|
|
10
|
+
recent, pins)
|
|
11
|
+
Each type exposes from_dict/to_dict (signatures below); no host imports.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
from dataclasses import asdict, dataclass, field
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class MemoryEntry:
|
|
19
|
+
id: str = ""
|
|
20
|
+
text: str = ""
|
|
21
|
+
role: str = "user"
|
|
22
|
+
session: str = ""
|
|
23
|
+
ts: float = 0.0
|
|
24
|
+
vector: list = field(default_factory=list)
|
|
25
|
+
importance: float = 0.5
|
|
26
|
+
pin: bool = False
|
|
27
|
+
metadata: dict = field(default_factory=dict)
|
|
28
|
+
scope: str = "default"
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def from_dict(cls, value):
|
|
32
|
+
if not isinstance(value, dict):
|
|
33
|
+
return cls(text=str(value or ""))
|
|
34
|
+
return cls(**{k: value[k] for k in cls.__dataclass_fields__ if k in value})
|
|
35
|
+
|
|
36
|
+
def to_dict(self):
|
|
37
|
+
out = asdict(self)
|
|
38
|
+
metadata = out.pop("metadata", {}) or {}
|
|
39
|
+
out.update(metadata)
|
|
40
|
+
return out
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class Cue:
|
|
45
|
+
entities: list = field(default_factory=list)
|
|
46
|
+
place: list = field(default_factory=list)
|
|
47
|
+
time: list = field(default_factory=list)
|
|
48
|
+
event_name: str = ""
|
|
49
|
+
id: str = ""
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def from_dict(cls, value):
|
|
53
|
+
if not isinstance(value, dict):
|
|
54
|
+
return cls()
|
|
55
|
+
return cls(**{k: value[k] for k in cls.__dataclass_fields__ if k in value})
|
|
56
|
+
|
|
57
|
+
def to_dict(self):
|
|
58
|
+
return asdict(self)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class EventVector:
|
|
63
|
+
intensity: float = 0.0
|
|
64
|
+
self_relevance: float = 0.0
|
|
65
|
+
schema_conflict: float = 0.0
|
|
66
|
+
relation_weight: float = 0.0
|
|
67
|
+
novelty: float = 0.0
|
|
68
|
+
unresolved: float = 0.0
|
|
69
|
+
repetition: float = 0.0
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def from_dict(cls, value):
|
|
73
|
+
if not isinstance(value, dict):
|
|
74
|
+
return cls()
|
|
75
|
+
return cls(**{k: value[k] for k in cls.__dataclass_fields__ if k in value})
|
|
76
|
+
|
|
77
|
+
def to_dict(self):
|
|
78
|
+
return asdict(self)
|
|
79
|
+
|
|
80
|
+
def as_dict(self):
|
|
81
|
+
"""Compatibility alias for to_dict (older call sites)."""
|
|
82
|
+
return self.to_dict()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class ThreadCard:
|
|
87
|
+
id: str = ""
|
|
88
|
+
text: str = ""
|
|
89
|
+
ts: float = 0.0
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def from_dict(cls, value):
|
|
93
|
+
if not isinstance(value, dict):
|
|
94
|
+
return cls()
|
|
95
|
+
return cls(**{k: value[k] for k in cls.__dataclass_fields__ if k in value})
|
|
96
|
+
|
|
97
|
+
def to_dict(self):
|
|
98
|
+
return asdict(self)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class RelayCard:
|
|
103
|
+
identity: list = field(default_factory=list)
|
|
104
|
+
relationship: list = field(default_factory=list)
|
|
105
|
+
open_loops: list = field(default_factory=list)
|
|
106
|
+
recent: list = field(default_factory=list)
|
|
107
|
+
pins: list = field(default_factory=list)
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def from_dict(cls, value):
|
|
111
|
+
if not isinstance(value, dict):
|
|
112
|
+
return cls()
|
|
113
|
+
return cls(**{k: value[k] for k in cls.__dataclass_fields__ if k in value})
|
|
114
|
+
|
|
115
|
+
def to_dict(self):
|
|
116
|
+
return asdict(self)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Normalization / hash / time helpers (stdlib only)."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import hashlib, re, time
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def sha12(text):
|
|
8
|
+
return hashlib.sha256(str(text).encode("utf-8")).hexdigest()[:12]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def clamp01(value, default=0.0):
|
|
12
|
+
try:
|
|
13
|
+
return max(0.0, min(1.0, float(value)))
|
|
14
|
+
except Exception:
|
|
15
|
+
return default
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def normalize_text(text):
|
|
19
|
+
return re.sub(r"\s+", " ", str(text or "")).strip()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def now_ts(now=None):
|
|
23
|
+
try:
|
|
24
|
+
return float(now if now is not None else time.time())
|
|
25
|
+
except Exception:
|
|
26
|
+
return time.time()
|