dsh-biocore 0.3.1 → 0.3.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/package.json +1 -1
- package/python/biocore_model/tests/test_extensions.py +167 -0
- package/python/biocore_model/tests/test_signals.py +106 -0
- package/python/biocore_runtime/extensions.py +317 -0
- package/python/biocore_runtime/pipeline.py +129 -0
- package/python/biocore_runtime/signals.py +243 -0
- package/python/data/signals.json +12247 -0
- package/ui/panel.css +123 -1
- package/ui/panel.html +2 -2
|
@@ -120,6 +120,20 @@ except Exception: # pragma: no cover
|
|
|
120
120
|
from biocore_runtime import relationship_state as _relationship_state
|
|
121
121
|
except Exception:
|
|
122
122
|
_relationship_state = None
|
|
123
|
+
try: # task-225: P0 memory + expression extensions
|
|
124
|
+
from . import extensions as _extensions
|
|
125
|
+
except Exception: # pragma: no cover
|
|
126
|
+
try:
|
|
127
|
+
from biocore_runtime import extensions as _extensions
|
|
128
|
+
except Exception:
|
|
129
|
+
_extensions = None
|
|
130
|
+
try: # task-224: P0 perception/relationship/self signals
|
|
131
|
+
from . import signals as _signals
|
|
132
|
+
except Exception: # pragma: no cover
|
|
133
|
+
try:
|
|
134
|
+
from biocore_runtime import signals as _signals
|
|
135
|
+
except Exception:
|
|
136
|
+
_signals = None
|
|
123
137
|
try: # task-222: self layer + autonomy
|
|
124
138
|
from . import self_layer as _self_layer
|
|
125
139
|
except Exception: # pragma: no cover
|
|
@@ -438,6 +452,21 @@ def _concept_filter(entries, query):
|
|
|
438
452
|
return narrowed or rows
|
|
439
453
|
|
|
440
454
|
|
|
455
|
+
def _signals_eval(store, session_id, user_text="", assistant_text="",
|
|
456
|
+
state=None, now=None):
|
|
457
|
+
if _signals is None or not store:
|
|
458
|
+
return None
|
|
459
|
+
try:
|
|
460
|
+
rel = (relationship_read(store)["relationship"]
|
|
461
|
+
if _store_root(store) is not None else {})
|
|
462
|
+
return _signals.evaluate(store, session_id=session_id,
|
|
463
|
+
user_text=user_text,
|
|
464
|
+
assistant_text=assistant_text,
|
|
465
|
+
state=state or {}, relationship=rel, now=now)
|
|
466
|
+
except Exception:
|
|
467
|
+
return None
|
|
468
|
+
|
|
469
|
+
|
|
441
470
|
def _governance_tick(store, now=None):
|
|
442
471
|
"""Decay/index/graph/aggregate sidecars; never deletes a memory."""
|
|
443
472
|
if _governance is None or not store:
|
|
@@ -670,6 +699,7 @@ def inject_block(session_id, store, max_chars=MAX_CHARS, top_k=TOP_K,
|
|
|
670
699
|
text_query = clean_text(query, MAX_TEXT_CHARS)
|
|
671
700
|
chosen = str(mode or "query").strip().lower()
|
|
672
701
|
entries = []
|
|
702
|
+
query_used = False
|
|
673
703
|
if chosen == "query" and text_query:
|
|
674
704
|
try:
|
|
675
705
|
picked = _query_entries(store, session_id, text_query, limit,
|
|
@@ -678,11 +708,24 @@ def inject_block(session_id, store, max_chars=MAX_CHARS, top_k=TOP_K,
|
|
|
678
708
|
picked = None
|
|
679
709
|
if picked:
|
|
680
710
|
entries = list(picked)
|
|
711
|
+
query_used = True
|
|
681
712
|
if not entries:
|
|
682
713
|
entries = sorted(_visible(load_memory(store, session_id).get("entries")),
|
|
683
714
|
key=lambda entry: (1 if entry.get("pin") else 0,
|
|
684
715
|
_num(entry.get("ts"), 0.0)),
|
|
685
716
|
reverse=True)[:limit]
|
|
717
|
+
if (query_used and text_query and entries and _extensions is not None):
|
|
718
|
+
try:
|
|
719
|
+
all_entries = _visible(load_memory(store, session_id).get("entries"))
|
|
720
|
+
ext = _extensions.expand_recall(
|
|
721
|
+
store, all_entries, text_query,
|
|
722
|
+
[str(entry.get("id") or "") for entry in entries])
|
|
723
|
+
for row in ext.get("extra") or []:
|
|
724
|
+
if row not in entries:
|
|
725
|
+
entries.append(row)
|
|
726
|
+
entries = entries[:max(limit, min(limit + 2, INJECT_MAX_K))]
|
|
727
|
+
except Exception:
|
|
728
|
+
pass
|
|
686
729
|
header = "你记得这些事:"
|
|
687
730
|
ids = []
|
|
688
731
|
if not entries or budget <= len(header) + 3:
|
|
@@ -824,6 +867,22 @@ def prompt_block(payload):
|
|
|
824
867
|
state_text = _state_block(state, budget)
|
|
825
868
|
constraints = _constraints_block(state, switches, data)
|
|
826
869
|
text = (state_text + "\n" + constraints).strip()
|
|
870
|
+
expression = None
|
|
871
|
+
if _extensions is not None:
|
|
872
|
+
try:
|
|
873
|
+
target = _int_param(data.get("length_target"), 0, 0, 400)
|
|
874
|
+
hard = _int_param(data.get("length_max"), 0, 0, 400)
|
|
875
|
+
expression = _extensions.expression_block(
|
|
876
|
+
data.get("store"), state, data.get("relationship"),
|
|
877
|
+
soft_target=target, soft_max=hard)
|
|
878
|
+
extra = [value for value in (expression.get("rules"),
|
|
879
|
+
expression.get("template"),
|
|
880
|
+
expression.get("topic"),
|
|
881
|
+
expression.get("voice")) if value]
|
|
882
|
+
if extra:
|
|
883
|
+
text = text + "\n" + "\n".join("- " + line for line in extra[:4])
|
|
884
|
+
except Exception:
|
|
885
|
+
expression = None
|
|
827
886
|
thinking_line = thinking_style_line(data)
|
|
828
887
|
persona_line = persona_prompt_line(data)
|
|
829
888
|
if thinking_line:
|
|
@@ -833,6 +892,8 @@ def prompt_block(payload):
|
|
|
833
892
|
sections = {"state": state_text, "constraints": constraints}
|
|
834
893
|
if thinking_line:
|
|
835
894
|
sections["thinking_style"] = thinking_line
|
|
895
|
+
if expression:
|
|
896
|
+
sections["expression"] = expression
|
|
836
897
|
if persona_line:
|
|
837
898
|
sections["persona"] = persona_line
|
|
838
899
|
return {"ok": True, "phase": phase, "text": text, "chars": len(text),
|
|
@@ -2166,11 +2227,25 @@ def _ui_state_body(store, session_id, data):
|
|
|
2166
2227
|
"limits": _autonomy.limits(store)}
|
|
2167
2228
|
except Exception:
|
|
2168
2229
|
autonomy_view = None
|
|
2230
|
+
signals_summary = None
|
|
2231
|
+
if _signals is not None:
|
|
2232
|
+
try:
|
|
2233
|
+
signals_summary = _signals.summary(store)
|
|
2234
|
+
except Exception:
|
|
2235
|
+
signals_summary = None
|
|
2236
|
+
extensions_summary = None
|
|
2237
|
+
if _extensions is not None:
|
|
2238
|
+
try:
|
|
2239
|
+
extensions_summary = _extensions.extension_summary(store)
|
|
2240
|
+
except Exception:
|
|
2241
|
+
extensions_summary = None
|
|
2169
2242
|
identity = {"v": round(_num(core.get("v"), 0.0), 2),
|
|
2170
2243
|
"intero": intero,
|
|
2171
2244
|
"governance": governance_summary,
|
|
2172
2245
|
"relationship_summary": relationship_summary,
|
|
2173
2246
|
"identity": identity_summary,
|
|
2247
|
+
"signals": signals_summary,
|
|
2248
|
+
"extensions": extensions_summary,
|
|
2174
2249
|
"available": True,
|
|
2175
2250
|
"session_id": sanitize_session_id(session_id),
|
|
2176
2251
|
"memory_count": len(entries),
|
|
@@ -2476,6 +2551,8 @@ def dispatch(action, payload=None):
|
|
|
2476
2551
|
data.get("state"))
|
|
2477
2552
|
if state_store is not None:
|
|
2478
2553
|
_save_session_state(state_store, session_id, session_state)
|
|
2554
|
+
_signals_eval(store, session_id, user_text=user_text,
|
|
2555
|
+
state=session_state, now=time.time())
|
|
2479
2556
|
capture_enabled = _as_bool(data.get("capture"), True)
|
|
2480
2557
|
captured_user = {"captured": False, "entry_id": "", "reason": "disabled"}
|
|
2481
2558
|
capture_store = data.get("store")
|
|
@@ -2607,6 +2684,51 @@ def dispatch(action, payload=None):
|
|
|
2607
2684
|
governance_info = _governance_tick(store, now=time.time())
|
|
2608
2685
|
relationship_info = _relationship_tick(
|
|
2609
2686
|
store, data.get("user_text"), now=time.time())
|
|
2687
|
+
signals_end = _signals_eval(
|
|
2688
|
+
store, session_id,
|
|
2689
|
+
user_text=str(data.get("user_text") or ""),
|
|
2690
|
+
assistant_text=assistant_text,
|
|
2691
|
+
state=(_load_session_state(store, session_id)
|
|
2692
|
+
if store is not None else {}),
|
|
2693
|
+
now=time.time())
|
|
2694
|
+
if _extensions is not None:
|
|
2695
|
+
try:
|
|
2696
|
+
all_entries = _visible(
|
|
2697
|
+
load_memory(store, session_id).get("entries"))
|
|
2698
|
+
picked_ids = [str(row.get("id") or "")
|
|
2699
|
+
for row in all_entries[:5]]
|
|
2700
|
+
commitment = ((signals_end or {}).get("relationship", {})
|
|
2701
|
+
.get("commitment_extractor") or {})
|
|
2702
|
+
if commitment.get("has_commitment"):
|
|
2703
|
+
_extensions.add_commitment(
|
|
2704
|
+
store, str(commitment.get("what") or ""),
|
|
2705
|
+
float(commitment.get("due_ts") or 0.0),
|
|
2706
|
+
now=time.time())
|
|
2707
|
+
expression = _extensions.expression_block(
|
|
2708
|
+
store, _load_session_state(store, session_id),
|
|
2709
|
+
(relationship_read(store)["relationship"]
|
|
2710
|
+
if store is not None else {}))
|
|
2711
|
+
snapshot = {
|
|
2712
|
+
"graph": {"entities": 0, "edges": 0},
|
|
2713
|
+
"layered": _extensions.layered_view(all_entries,
|
|
2714
|
+
picked_ids),
|
|
2715
|
+
"prospective": _extensions.prospective_view(
|
|
2716
|
+
store, time.time()),
|
|
2717
|
+
"reconstruction": _extensions.reconstruction_view(
|
|
2718
|
+
all_entries),
|
|
2719
|
+
"turn_memory": _extensions.turn_memory_view(
|
|
2720
|
+
[{"entry_id": row.get("id"),
|
|
2721
|
+
"text": row.get("text"), "source": "recall"}
|
|
2722
|
+
for row in all_entries[:5]],
|
|
2723
|
+
reply=assistant_text, now=time.time()),
|
|
2724
|
+
"expression": expression,
|
|
2725
|
+
}
|
|
2726
|
+
hits, graph_stats = _extensions.graph_hits(
|
|
2727
|
+
all_entries, str(data.get("user_text") or ""))
|
|
2728
|
+
snapshot["graph"] = graph_stats
|
|
2729
|
+
_extensions.record_extension_snapshot(store, snapshot)
|
|
2730
|
+
except Exception:
|
|
2731
|
+
pass
|
|
2610
2732
|
identity_info = None
|
|
2611
2733
|
if relationship_info:
|
|
2612
2734
|
identity_info = _identity_tick(
|
|
@@ -2677,11 +2799,18 @@ def dispatch(action, payload=None):
|
|
|
2677
2799
|
"limits": _autonomy.limits(store)}
|
|
2678
2800
|
except Exception:
|
|
2679
2801
|
autonomy_info = None
|
|
2802
|
+
signals_info = None
|
|
2803
|
+
if _signals is not None:
|
|
2804
|
+
try:
|
|
2805
|
+
signals_info = _signals.summary(store)
|
|
2806
|
+
except Exception:
|
|
2807
|
+
signals_info = None
|
|
2680
2808
|
return {"ok": True, "embed": embed, "switches": out,
|
|
2681
2809
|
"governance": governance_info,
|
|
2682
2810
|
"relationship": relationship_info,
|
|
2683
2811
|
"identity": identity_info,
|
|
2684
2812
|
"autonomy": autonomy_info,
|
|
2813
|
+
"signals": signals_info,
|
|
2685
2814
|
"store": str(store)}
|
|
2686
2815
|
if action == "autonomy_tick":
|
|
2687
2816
|
return _autonomy_tick(store, data) or {
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""task-224 (5a): P0 perception / relationship / self modules wired into the core.
|
|
3
|
+
|
|
4
|
+
One deterministic snapshot per turn, stored at <dataDir>/signals.json and
|
|
5
|
+
exposed through /state. Every module call is guarded: a missing or failing
|
|
6
|
+
module yields {"enabled": false, "reason": ...} instead of breaking a turn.
|
|
7
|
+
No LLM, no IO beyond the one sidecar file.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import importlib
|
|
12
|
+
import json
|
|
13
|
+
import time
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import biocore_model as _bm
|
|
17
|
+
|
|
18
|
+
SIGNALS_FILE = "signals.json"
|
|
19
|
+
HISTORY_LIMIT = 50
|
|
20
|
+
SCHEMA = 1
|
|
21
|
+
|
|
22
|
+
PERCEPTION = ("affective_labor", "compassion_fatigue", "connection_bids",
|
|
23
|
+
"defense_mechanisms", "emotion_geometry", "needs_events",
|
|
24
|
+
"social_pain", "social_repair")
|
|
25
|
+
RELATIONSHIP = ("commitment_extractor", "relation_stage",
|
|
26
|
+
"relationship_stage", "repair_after_boundary")
|
|
27
|
+
SELF = ("metacog", "possible_selves", "self_interest", "self_verification")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _root(store):
|
|
31
|
+
if isinstance(store, dict):
|
|
32
|
+
raw = store.get("root") or store.get("store") or store.get("path")
|
|
33
|
+
else:
|
|
34
|
+
raw = store
|
|
35
|
+
text = str(raw or "").strip()
|
|
36
|
+
return Path(text) if text else None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _data_dir(store):
|
|
40
|
+
root = _root(store)
|
|
41
|
+
return (root.parent if root.name == "memory" else root) if root else None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _read(path):
|
|
45
|
+
try:
|
|
46
|
+
if path is None or not path.exists():
|
|
47
|
+
return {}
|
|
48
|
+
doc = json.loads(path.read_text(encoding="utf-8"))
|
|
49
|
+
return doc if isinstance(doc, dict) else {}
|
|
50
|
+
except Exception:
|
|
51
|
+
return {}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _write(path, doc):
|
|
55
|
+
try:
|
|
56
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
tmp = path.with_name(path.name + ".tmp")
|
|
58
|
+
tmp.write_text(json.dumps(doc, ensure_ascii=False, indent=2),
|
|
59
|
+
encoding="utf-8")
|
|
60
|
+
tmp.replace(path)
|
|
61
|
+
return True
|
|
62
|
+
except Exception:
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _mod(name):
|
|
67
|
+
module = getattr(_bm, name, None)
|
|
68
|
+
if module is not None:
|
|
69
|
+
return module
|
|
70
|
+
try:
|
|
71
|
+
return importlib.import_module("biocore_model." + name)
|
|
72
|
+
except Exception:
|
|
73
|
+
try:
|
|
74
|
+
return importlib.import_module("." + name, package="biocore_model")
|
|
75
|
+
except Exception:
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _call(name, fn, *args, **kwargs):
|
|
80
|
+
module = _mod(name)
|
|
81
|
+
if module is None or not callable(getattr(module, fn, None)):
|
|
82
|
+
return {"enabled": False, "reason": "module_missing"}
|
|
83
|
+
try:
|
|
84
|
+
value = getattr(module, fn)(*args, **kwargs)
|
|
85
|
+
return {"enabled": True, "value": value}
|
|
86
|
+
except Exception as error:
|
|
87
|
+
return {"enabled": False, "reason": type(error).__name__}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def evaluate(store, session_id="s1", user_text="", assistant_text="",
|
|
91
|
+
state=None, relationship=None, now=None):
|
|
92
|
+
"""Run all 17 P0 modules once; never raises."""
|
|
93
|
+
stamp = round(float(now or time.time()), 3)
|
|
94
|
+
core = state.get("core") if isinstance(state, dict) and isinstance(
|
|
95
|
+
state.get("core"), dict) else (state if isinstance(state, dict) else {})
|
|
96
|
+
rel = relationship if isinstance(relationship, dict) else {}
|
|
97
|
+
v = float(core.get("v") or 0.0)
|
|
98
|
+
a = float(core.get("a") or 0.5)
|
|
99
|
+
stress = float(core.get("stress") or 0.2)
|
|
100
|
+
social = float(core.get("social") or 0.55)
|
|
101
|
+
text = str(user_text or "")
|
|
102
|
+
|
|
103
|
+
out = {"schema": SCHEMA, "ts": stamp, "session_id": str(session_id),
|
|
104
|
+
"enabled": {}, "perception": {}, "relationship": {}, "self": {}}
|
|
105
|
+
|
|
106
|
+
# --- perception ---------------------------------------------------
|
|
107
|
+
labor = _call("affective_labor", "turn_cost", max(0.0, min(1.0, (v + 1) / 2)),
|
|
108
|
+
max(0.0, min(1.0, (v + 1) / 2)), 0.0)
|
|
109
|
+
out["perception"]["affective_labor"] = labor
|
|
110
|
+
out["enabled"]["affective_labor"] = labor["enabled"]
|
|
111
|
+
|
|
112
|
+
fatigue = _call("compassion_fatigue", "fatigue", 0, max(0.0, -v), 0.0)
|
|
113
|
+
out["perception"]["compassion_fatigue"] = fatigue
|
|
114
|
+
out["enabled"]["compassion_fatigue"] = fatigue["enabled"]
|
|
115
|
+
|
|
116
|
+
recorded = _call("connection_bids", "record_bid", [], stamp, "user")
|
|
117
|
+
bid_items = recorded.get("value") if isinstance(recorded.get("value"), list) else []
|
|
118
|
+
settled = _call("connection_bids", "settle_bids", bid_items, stamp)
|
|
119
|
+
view = _call("connection_bids", "bid_view", bid_items, 1.0)
|
|
120
|
+
out["perception"]["connection_bids"] = {
|
|
121
|
+
"enabled": recorded["enabled"], "bids": len(bid_items),
|
|
122
|
+
"settled": len(settled.get("value") or []) if isinstance(
|
|
123
|
+
settled.get("value"), list) else 0,
|
|
124
|
+
"view": view.get("value")}
|
|
125
|
+
out["enabled"]["connection_bids"] = recorded["enabled"]
|
|
126
|
+
|
|
127
|
+
defense = _call("defense_mechanisms", "assess",
|
|
128
|
+
{"kind": "message", "valence": v, "intensity": abs(v)},
|
|
129
|
+
{"valence": v, "arousal": a, "stress": stress})
|
|
130
|
+
out["perception"]["defense_mechanisms"] = defense
|
|
131
|
+
out["enabled"]["defense_mechanisms"] = defense["enabled"]
|
|
132
|
+
|
|
133
|
+
geometry = _call("emotion_geometry", "classify",
|
|
134
|
+
{"v": v, "a": a, "stress": stress})
|
|
135
|
+
out["perception"]["emotion_geometry"] = geometry
|
|
136
|
+
out["enabled"]["emotion_geometry"] = geometry["enabled"]
|
|
137
|
+
|
|
138
|
+
needs = _call("needs_events", "needs_state",
|
|
139
|
+
{"social": social, "energy": core.get("energy", 0.6),
|
|
140
|
+
"stress": stress})
|
|
141
|
+
out["perception"]["needs_events"] = needs
|
|
142
|
+
out["enabled"]["needs_events"] = needs["enabled"]
|
|
143
|
+
|
|
144
|
+
pain = _call("social_pain", "update", {"value": 0.0},
|
|
145
|
+
"rejection" if v < -0.3 or rel.get("conflict", 0) > 0.2
|
|
146
|
+
else "neutral", stamp)
|
|
147
|
+
pain_view = _call("social_pain", "view", pain.get("value") or {"value": 0.0})
|
|
148
|
+
out["perception"]["social_pain"] = {
|
|
149
|
+
"enabled": pain["enabled"], "view": pain_view.get("value")}
|
|
150
|
+
out["enabled"]["social_pain"] = pain["enabled"]
|
|
151
|
+
|
|
152
|
+
repair = _call("social_repair", "repair_check", text, "")
|
|
153
|
+
empathy = _call("social_repair", "empathy", v)
|
|
154
|
+
out["perception"]["social_repair"] = {
|
|
155
|
+
"enabled": repair["enabled"], "repair": repair.get("value"),
|
|
156
|
+
"empathy": empathy.get("value")}
|
|
157
|
+
out["enabled"]["social_repair"] = repair["enabled"]
|
|
158
|
+
|
|
159
|
+
# --- relationship -------------------------------------------------
|
|
160
|
+
commitment = _call("commitment_extractor", "extract", text, None, stamp)
|
|
161
|
+
value = commitment.get("value") if isinstance(commitment.get("value"), dict) else {}
|
|
162
|
+
out["relationship"]["commitment_extractor"] = {
|
|
163
|
+
"enabled": commitment["enabled"],
|
|
164
|
+
"has_commitment": bool(value.get("has_commitment")),
|
|
165
|
+
"what": str(value.get("what") or ""),
|
|
166
|
+
"due_ts": value.get("due_ts"),
|
|
167
|
+
"value": value}
|
|
168
|
+
out["enabled"]["commitment_extractor"] = commitment["enabled"]
|
|
169
|
+
|
|
170
|
+
stage = _call("relation_stage", "evaluate",
|
|
171
|
+
{"valence": v, "stress": stress, "relationship": rel})
|
|
172
|
+
out["relationship"]["relation_stage"] = stage
|
|
173
|
+
out["enabled"]["relation_stage"] = stage["enabled"]
|
|
174
|
+
|
|
175
|
+
hours = float(rel.get("days_known", 0) or 0) * 24.0
|
|
176
|
+
stage_word = _call("relationship_stage", "stage_for", hours)
|
|
177
|
+
out["relationship"]["relationship_stage"] = stage_word
|
|
178
|
+
out["enabled"]["relationship_stage"] = stage_word["enabled"]
|
|
179
|
+
|
|
180
|
+
boundary = _call("repair_after_boundary", "evaluate", None, 0.0, None)
|
|
181
|
+
out["relationship"]["repair_after_boundary"] = boundary
|
|
182
|
+
out["enabled"]["repair_after_boundary"] = boundary["enabled"]
|
|
183
|
+
|
|
184
|
+
# --- self ---------------------------------------------------------
|
|
185
|
+
band = _call("metacog", "evaluate", None,
|
|
186
|
+
max(0.0, min(1.0, 1.0 - abs(v))), 0,
|
|
187
|
+
bool(rel.get("conflict", 0) > 0.3))
|
|
188
|
+
out["self"]["metacog"] = band
|
|
189
|
+
out["enabled"]["metacog"] = band["enabled"]
|
|
190
|
+
|
|
191
|
+
selves = _call("possible_selves", "rank", None,
|
|
192
|
+
{"valence": v, "stress": stress})
|
|
193
|
+
out["self"]["possible_selves"] = selves
|
|
194
|
+
out["enabled"]["possible_selves"] = selves["enabled"]
|
|
195
|
+
|
|
196
|
+
interest = _call("self_interest", "assess",
|
|
197
|
+
{"valence": v, "social": social,
|
|
198
|
+
"relationship": rel, "needs": core.get("needs")})
|
|
199
|
+
out["self"]["self_interest"] = interest
|
|
200
|
+
out["enabled"]["self_interest"] = interest["enabled"]
|
|
201
|
+
|
|
202
|
+
verify = _call("self_verification", "observe", {"misread": 0.0},
|
|
203
|
+
0.0, 0.6, float(rel.get("trust", 0.5) or 0.5))
|
|
204
|
+
verify_intent = _call("self_verification", "intent",
|
|
205
|
+
verify.get("value") or {"value": 0.0})
|
|
206
|
+
out["self"]["self_verification"] = {
|
|
207
|
+
"enabled": verify["enabled"], "state": verify.get("value"),
|
|
208
|
+
"intent": verify_intent.get("value")}
|
|
209
|
+
out["enabled"]["self_verification"] = verify["enabled"]
|
|
210
|
+
|
|
211
|
+
out["counts"] = {
|
|
212
|
+
"perception": len(out["perception"]),
|
|
213
|
+
"relationship": len(out["relationship"]),
|
|
214
|
+
"self": len(out["self"]),
|
|
215
|
+
"enabled": sum(1 for value in out["enabled"].values() if value),
|
|
216
|
+
}
|
|
217
|
+
_record(store, session_id, out)
|
|
218
|
+
return out
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _record(store, session_id, snapshot):
|
|
222
|
+
path = (_data_dir(store) or Path(".")) / SIGNALS_FILE
|
|
223
|
+
doc = _read(path)
|
|
224
|
+
history = [row for row in (doc.get("history") or []) if isinstance(row, dict)]
|
|
225
|
+
history.append(snapshot)
|
|
226
|
+
doc.update({"schema": SCHEMA, "ts": snapshot["ts"],
|
|
227
|
+
"session_id": str(session_id),
|
|
228
|
+
"history": history[-HISTORY_LIMIT:],
|
|
229
|
+
"latest": snapshot})
|
|
230
|
+
_write(path, doc)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def summary(store):
|
|
234
|
+
"""Read-only snapshot for /state (never recomputes)."""
|
|
235
|
+
doc = _read((_data_dir(store) or Path(".")) / SIGNALS_FILE)
|
|
236
|
+
latest = doc.get("latest") if isinstance(doc.get("latest"), dict) else {}
|
|
237
|
+
if not latest:
|
|
238
|
+
return {"enabled": {name: False for name in
|
|
239
|
+
PERCEPTION + RELATIONSHIP + SELF},
|
|
240
|
+
"perception": {}, "relationship": {}, "self": {},
|
|
241
|
+
"counts": {"perception": 0, "relationship": 0, "self": 0,
|
|
242
|
+
"enabled": 0}}
|
|
243
|
+
return latest
|