dsh-biocore 0.1.2 → 0.1.4
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/lib/index.js +170 -27
- package/package.json +1 -1
- package/python/biocore_runtime/pipeline.py +42 -0
- package/python/biocore_runtime/rerank.py +201 -0
- package/python/mindbase/__init__.py +18 -0
- package/python/mindbase/__main__.py +3 -0
- package/python/mindbase/actions.py +56 -0
- package/python/mindbase/active_inference.py +54 -0
- package/python/mindbase/addressing.py +69 -0
- package/python/mindbase/affect.py +86 -0
- package/python/mindbase/affective.py +111 -0
- package/python/mindbase/allostasis.py +28 -0
- package/python/mindbase/appraisal.py +101 -0
- package/python/mindbase/attachment.py +49 -0
- package/python/mindbase/attention.py +30 -0
- package/python/mindbase/autonomy.py +81 -0
- package/python/mindbase/autonomy_bench.py +251 -0
- package/python/mindbase/autonomy_evolve.py +342 -0
- package/python/mindbase/autonomy_loop.py +65 -0
- package/python/mindbase/autonomy_meta.py +91 -0
- package/python/mindbase/base_only.py +28 -0
- package/python/mindbase/benchmark.py +61 -0
- package/python/mindbase/bio_adaptation.py +274 -0
- package/python/mindbase/bio_noise.py +39 -0
- package/python/mindbase/bio_semantic.py +138 -0
- package/python/mindbase/bm25.py +46 -0
- package/python/mindbase/bootstrap.py +256 -0
- package/python/mindbase/bootstrap_real.py +231 -0
- package/python/mindbase/brain.py +178 -0
- package/python/mindbase/care.py +78 -0
- package/python/mindbase/chaos_search.py +79 -0
- package/python/mindbase/character_profile.py +33 -0
- package/python/mindbase/circadian.py +38 -0
- package/python/mindbase/circuit.py +235 -0
- package/python/mindbase/clock.py +53 -0
- package/python/mindbase/companion_runtime.py +366 -0
- package/python/mindbase/companion_train.py +126 -0
- package/python/mindbase/config.py +66 -0
- package/python/mindbase/connection_state.py +76 -0
- package/python/mindbase/connectome_prior.py +35 -0
- package/python/mindbase/connectome_world.py +158 -0
- package/python/mindbase/constructs.py +88 -0
- package/python/mindbase/context_builder.py +58 -0
- package/python/mindbase/critical.py +36 -0
- package/python/mindbase/criticality.py +123 -0
- package/python/mindbase/defaults.py +27 -0
- package/python/mindbase/demo.py +163 -0
- package/python/mindbase/diary.py +116 -0
- package/python/mindbase/diary_service.py +40 -0
- package/python/mindbase/digital_prior.py +37 -0
- package/python/mindbase/diversity.py +110 -0
- package/python/mindbase/drives.py +106 -0
- package/python/mindbase/embedding.py +132 -0
- package/python/mindbase/embedding_memory.py +74 -0
- package/python/mindbase/embodiment.py +83 -0
- package/python/mindbase/emotion_calibration.py +39 -0
- package/python/mindbase/emotion_calibrator.py +50 -0
- package/python/mindbase/emotion_core.py +113 -0
- package/python/mindbase/emotion_dynamics.py +104 -0
- package/python/mindbase/emotion_intent.py +164 -0
- package/python/mindbase/emotion_layer.py +200 -0
- package/python/mindbase/emotion_schema.py +92 -0
- package/python/mindbase/engine.py +781 -0
- package/python/mindbase/episode_log.py +90 -0
- package/python/mindbase/evaluation.py +493 -0
- package/python/mindbase/event_appraisal.py +84 -0
- package/python/mindbase/evolution.py +77 -0
- package/python/mindbase/evolve_offline.py +232 -0
- package/python/mindbase/experiments.py +358 -0
- package/python/mindbase/expression.py +54 -0
- package/python/mindbase/external_brain.py +157 -0
- package/python/mindbase/fly_mb.py +159 -0
- package/python/mindbase/fly_tasks.py +45 -0
- package/python/mindbase/forage.py +179 -0
- package/python/mindbase/goal_generator.py +98 -0
- package/python/mindbase/goals.py +153 -0
- package/python/mindbase/grid_world.py +123 -0
- package/python/mindbase/group_autonomy.py +92 -0
- package/python/mindbase/group_context.py +67 -0
- package/python/mindbase/homeostatic.py +71 -0
- package/python/mindbase/homeostatic_action.py +38 -0
- package/python/mindbase/human_standard.py +53 -0
- package/python/mindbase/identity_suite.py +50 -0
- package/python/mindbase/image_context.py +27 -0
- package/python/mindbase/impression.py +119 -0
- package/python/mindbase/initiative_quota.py +145 -0
- package/python/mindbase/insight.py +51 -0
- package/python/mindbase/intelligence_search.py +108 -0
- package/python/mindbase/intention.py +76 -0
- package/python/mindbase/interaction_log.py +30 -0
- package/python/mindbase/interoception.py +67 -0
- package/python/mindbase/invariants.py +53 -0
- package/python/mindbase/iteration.py +29 -0
- package/python/mindbase/layered_memory.py +170 -0
- package/python/mindbase/layers/__init__.py +18 -0
- package/python/mindbase/layers/l0_substrate.py +39 -0
- package/python/mindbase/layers/l1_homeostasis.py +6 -0
- package/python/mindbase/layers/l2_affect.py +6 -0
- package/python/mindbase/layers/l3_attention.py +6 -0
- package/python/mindbase/layers/l4_learning.py +17 -0
- package/python/mindbase/layers/l5_self_social.py +35 -0
- package/python/mindbase/layers/l6_expression.py +23 -0
- package/python/mindbase/layers/l7_evaluation.py +45 -0
- package/python/mindbase/learner.py +196 -0
- package/python/mindbase/lexicon.py +33 -0
- package/python/mindbase/life_state.py +169 -0
- package/python/mindbase/lifecycle.py +87 -0
- package/python/mindbase/liquid_readout.py +48 -0
- package/python/mindbase/llm_adapter.py +233 -0
- package/python/mindbase/llm_cache.py +77 -0
- package/python/mindbase/llm_gate.py +85 -0
- package/python/mindbase/llm_openai.py +130 -0
- package/python/mindbase/llm_setup.py +99 -0
- package/python/mindbase/longrun.py +83 -0
- package/python/mindbase/longterm_memory.py +606 -0
- package/python/mindbase/mb/__init__.py +14 -0
- package/python/mindbase/mb/apl.py +22 -0
- package/python/mindbase/mb/dan.py +14 -0
- package/python/mindbase/mb/habituation.py +27 -0
- package/python/mindbase/mb/kc.py +28 -0
- package/python/mindbase/mb/mbon.py +23 -0
- package/python/mindbase/mb/mushroom_body.py +116 -0
- package/python/mindbase/memory.py +201 -0
- package/python/mindbase/memory_framework.py +158 -0
- package/python/mindbase/memory_retriever.py +110 -0
- package/python/mindbase/memory_summarizer.py +177 -0
- package/python/mindbase/memory_task.py +32 -0
- package/python/mindbase/memory_trace.py +46 -0
- package/python/mindbase/meta.py +32 -0
- package/python/mindbase/motivation.py +31 -0
- package/python/mindbase/multi_session.py +19 -0
- package/python/mindbase/narrative.py +87 -0
- package/python/mindbase/needs.py +77 -0
- package/python/mindbase/neural.py +45 -0
- package/python/mindbase/neuron_models.py +134 -0
- package/python/mindbase/noise.py +309 -0
- package/python/mindbase/one_on_one.py +60 -0
- package/python/mindbase/online_iteration.py +63 -0
- package/python/mindbase/ostracism.py +35 -0
- package/python/mindbase/parallel_eval.py +60 -0
- package/python/mindbase/parallel_qd.py +64 -0
- package/python/mindbase/persona.py +221 -0
- package/python/mindbase/persona_ab.py +64 -0
- package/python/mindbase/persona_bridge.py +129 -0
- package/python/mindbase/persona_compile.py +34 -0
- package/python/mindbase/persona_dsh.py +120 -0
- package/python/mindbase/persona_learner.py +157 -0
- package/python/mindbase/physiology.py +197 -0
- package/python/mindbase/population_lifecycle.py +83 -0
- package/python/mindbase/preferences.py +50 -0
- package/python/mindbase/proactive_agent.py +274 -0
- package/python/mindbase/proactive_gate.py +53 -0
- package/python/mindbase/proactive_group.py +119 -0
- package/python/mindbase/proactive_policy.py +422 -0
- package/python/mindbase/proactive_profile.py +196 -0
- package/python/mindbase/proactive_timing.py +28 -0
- package/python/mindbase/pyramid_stages.py +61 -0
- package/python/mindbase/quantum.py +132 -0
- package/python/mindbase/quantum_chaos.py +98 -0
- package/python/mindbase/reflection.py +68 -0
- package/python/mindbase/regulation_posture.py +50 -0
- package/python/mindbase/relationship_session.py +431 -0
- package/python/mindbase/relationship_suite.py +45 -0
- package/python/mindbase/relationships.py +70 -0
- package/python/mindbase/replay.py +22 -0
- package/python/mindbase/reply_diversity.py +53 -0
- package/python/mindbase/reply_gate.py +52 -0
- package/python/mindbase/rerank.py +41 -0
- package/python/mindbase/rerank_local.py +46 -0
- package/python/mindbase/ripple.py +187 -0
- package/python/mindbase/robust_search.py +68 -0
- package/python/mindbase/runtime.py +113 -0
- package/python/mindbase/scoped_identity.py +116 -0
- package/python/mindbase/self_insight.py +51 -0
- package/python/mindbase/self_model.py +116 -0
- package/python/mindbase/selftest.py +118 -0
- package/python/mindbase/semantic_memory.py +108 -0
- package/python/mindbase/session_store.py +175 -0
- package/python/mindbase/session_window.py +31 -0
- package/python/mindbase/shadow_eval.py +42 -0
- package/python/mindbase/sim_env.py +86 -0
- package/python/mindbase/social.py +138 -0
- package/python/mindbase/social_constraint.py +141 -0
- package/python/mindbase/social_feedback.py +95 -0
- package/python/mindbase/social_norm.py +225 -0
- package/python/mindbase/spiking_reservoir.py +204 -0
- package/python/mindbase/spontaneity.py +40 -0
- package/python/mindbase/story_loop.py +203 -0
- package/python/mindbase/survival_gauntlet.py +128 -0
- package/python/mindbase/symbolic.py +170 -0
- package/python/mindbase/teacher.py +244 -0
- package/python/mindbase/theory.py +42 -0
- package/python/mindbase/theory_of_mind.py +98 -0
- package/python/mindbase/translation_layer.py +80 -0
- package/python/mindbase/turn_eval.py +52 -0
- package/python/mindbase/types.py +78 -0
- package/python/mindbase/uncertainty_field.py +61 -0
- package/python/mindbase/user_loop.py +45 -0
- package/python/mindbase/user_profile.py +73 -0
- package/python/mindbase/value_model.py +64 -0
- package/python/mindbase/values.py +102 -0
- package/python/mindbase/vector_store.py +78 -0
- package/python/mindbase/viability.py +41 -0
- package/python/mindbase/virtual_body.py +90 -0
- package/python/mindbase/will.py +60 -0
- package/python/mindbase/working_memory.py +32 -0
- package/python/mindbase/workspace.py +55 -0
- package/ui/panel.css +16 -5
- package/ui/panel.html +25 -1
- package/ui/panel.js +57 -15
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Cheap social-addressing inference.
|
|
2
|
+
|
|
3
|
+
This is not a semantic world model. It only answers:
|
|
4
|
+
"is this message plausibly aimed at her?"
|
|
5
|
+
Explicit @/name is handled by the bridge; this fills the implicit gap:
|
|
6
|
+
continuations after her reply, second-person questions, backchannels.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
ALIASES = ("灰羽", "能天使", "阿能", "小乐", "蕾缪乐")
|
|
13
|
+
_AT = re.compile(r"@[^\s((]+")
|
|
14
|
+
_SECOND_PERSON = ("你", "您")
|
|
15
|
+
_QUESTION = ("吗", "呢", "怎么", "为什么", "啥", "什么", "哪", "谁", "吗?", "?", "?")
|
|
16
|
+
_CONTINUE = ("然后", "所以", "那", "可是", "但是", "刚才", "继续", "你说", "你觉得", "你看")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def infer_addressing(rows, text: str, speaker: str = "") -> dict:
|
|
20
|
+
t = str(text or "").strip()
|
|
21
|
+
low = t.lower()
|
|
22
|
+
score = 0.0
|
|
23
|
+
reasons = []
|
|
24
|
+
for a in ALIASES:
|
|
25
|
+
if a in t:
|
|
26
|
+
score = 1.0
|
|
27
|
+
reasons.append("name:" + a)
|
|
28
|
+
break
|
|
29
|
+
if score >= 1.0:
|
|
30
|
+
return {"score": 1.0, "addressed": True, "reasons": reasons}
|
|
31
|
+
ats = _AT.findall(t)
|
|
32
|
+
if ats:
|
|
33
|
+
# @ someone else is a strong negative unless an alias is also present
|
|
34
|
+
score -= 0.35
|
|
35
|
+
reasons.append("at_other")
|
|
36
|
+
prev = {}
|
|
37
|
+
try:
|
|
38
|
+
if len(rows) >= 2:
|
|
39
|
+
prev = rows[-2]
|
|
40
|
+
except Exception:
|
|
41
|
+
prev = {}
|
|
42
|
+
prev_reply = str(prev.get("reply") or "").strip() if isinstance(prev, dict) else ""
|
|
43
|
+
if prev_reply:
|
|
44
|
+
score += 0.35
|
|
45
|
+
reasons.append("after_her_reply")
|
|
46
|
+
if any(w in t for w in _SECOND_PERSON):
|
|
47
|
+
score += 0.20
|
|
48
|
+
reasons.append("second_person")
|
|
49
|
+
if any(w in t for w in _QUESTION):
|
|
50
|
+
score += 0.15
|
|
51
|
+
reasons.append("question")
|
|
52
|
+
if any(w in t for w in _CONTINUE):
|
|
53
|
+
score += 0.12
|
|
54
|
+
reasons.append("continuation")
|
|
55
|
+
if len(t) <= 8 and t and not ats:
|
|
56
|
+
score += 0.08
|
|
57
|
+
reasons.append("short")
|
|
58
|
+
# previous row spoken by someone else, current speaker is a different person:
|
|
59
|
+
# this is often a dyadic exchange, not aimed at her.
|
|
60
|
+
try:
|
|
61
|
+
if len(rows) >= 2:
|
|
62
|
+
psp = str(rows[-2].get("speaker") or "")
|
|
63
|
+
if psp and speaker and psp != speaker:
|
|
64
|
+
score -= 0.10
|
|
65
|
+
reasons.append("other_speaker")
|
|
66
|
+
except Exception:
|
|
67
|
+
pass
|
|
68
|
+
score = max(0.0, min(1.0, score))
|
|
69
|
+
return {"score": round(score, 3), "addressed": bool(score >= 0.55), "reasons": reasons}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from typing import Dict
|
|
5
|
+
|
|
6
|
+
from .config import MindConfig
|
|
7
|
+
from .noise import clip, ou
|
|
8
|
+
from .types import Appraisal
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Affect:
|
|
12
|
+
"""Fast affect (V/A/stress) + slow mood + neuromodulator scalars."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, cfg: MindConfig, rng: random.Random):
|
|
15
|
+
self.cfg = cfg
|
|
16
|
+
self.rng = rng
|
|
17
|
+
self.v = 0.0
|
|
18
|
+
self.a = 0.5
|
|
19
|
+
self.dominance = 0.0
|
|
20
|
+
self.stress = 0.0
|
|
21
|
+
self.scar = 0.0
|
|
22
|
+
self.mood = 0.0
|
|
23
|
+
self.fatigue = 0.0
|
|
24
|
+
self.da = 0.5
|
|
25
|
+
self.oa = 0.5
|
|
26
|
+
self.ht = 0.5
|
|
27
|
+
|
|
28
|
+
def update(self, ap: Appraisal, drives, dt: float) -> None:
|
|
29
|
+
v_target = clip(0.60 * ap.valence + 0.20 * self.mood + 0.20 * ap.social, -1.0, 1.0)
|
|
30
|
+
a_target = clip(
|
|
31
|
+
0.40 + 0.30 * ap.novelty + 0.20 * ap.threat + 0.10 * (1.0 - drives.get("energy")),
|
|
32
|
+
0.0,
|
|
33
|
+
1.0,
|
|
34
|
+
)
|
|
35
|
+
stress_target = clip(
|
|
36
|
+
0.50 * ap.threat + 0.30 * max(0.0, -ap.valence) + 0.20 * (1.0 - drives.get("safety")),
|
|
37
|
+
0.0,
|
|
38
|
+
1.0,
|
|
39
|
+
)
|
|
40
|
+
theta_fast = 1.0 / max(self.cfg.affect_tau, 1e-6)
|
|
41
|
+
theta_stress = 1.0 / max(self.cfg.stress_tau, 1e-6)
|
|
42
|
+
theta_mood = 1.0 / max(self.cfg.mood_tau, 1e-6)
|
|
43
|
+
self.v = ou(self.v, v_target, theta_fast, self.cfg.noise_sigma, dt, self.rng)
|
|
44
|
+
self.a = ou(self.a, a_target, theta_fast, self.cfg.noise_sigma, dt, self.rng)
|
|
45
|
+
self.stress = ou(self.stress, stress_target, theta_stress, self.cfg.noise_sigma, dt, self.rng)
|
|
46
|
+
dominance_target = clip(0.5 * ap.agency + 0.25 * ap.control + 0.25 * (1.0 - ap.threat) - 0.2 * self.stress, -1.0, 1.0)
|
|
47
|
+
self.dominance = ou(self.dominance, dominance_target, theta_fast, self.cfg.noise_sigma * 0.5, dt, self.rng)
|
|
48
|
+
self.dominance = clip(self.dominance, -1.0, 1.0)
|
|
49
|
+
mood_target = clip(0.70 * self.v + 0.30 * self.mood, -1.0, 1.0)
|
|
50
|
+
self.mood = ou(self.mood, mood_target, theta_mood, self.cfg.noise_sigma * 0.5, dt, self.rng)
|
|
51
|
+
self.v = clip(self.v, -1.0, 1.0)
|
|
52
|
+
self.a = clip(self.a, 0.0, 1.0)
|
|
53
|
+
self.stress = clip(self.stress, 0.0, 1.0)
|
|
54
|
+
# acute load can recover; a slow scar accumulates under chronic stress
|
|
55
|
+
grow = 0.00002 * dt * max(0.0, self.stress - self.scar)
|
|
56
|
+
decay = 0.0000002 * dt * self.scar
|
|
57
|
+
self.scar = clip(self.scar + grow - decay, 0.0, 1.0)
|
|
58
|
+
self.mood = clip(self.mood, -1.0, 1.0)
|
|
59
|
+
# neuromodulators: DA tracks RPE, OA tracks arousal, 5-HT tracks patience
|
|
60
|
+
self.da = clip(self.da + 0.08 * (ap.rpe - 0.10 * self.da), 0.0, 1.0)
|
|
61
|
+
self.oa = clip(self.oa + 0.08 * (self.a - self.oa), 0.0, 1.0)
|
|
62
|
+
self.ht = clip(self.ht + 0.03 * ((1.0 - self.stress) - self.ht), 0.0, 1.0)
|
|
63
|
+
|
|
64
|
+
def update_structural_load(self, load: float, dt: float) -> None:
|
|
65
|
+
# crowded/overloaded circuitry adds metabolic fatigue
|
|
66
|
+
self.fatigue = clip(self.fatigue + 0.002 * max(0.0, float(load)) * (max(0.0, float(dt)) / 3600.0), 0.0, 1.0)
|
|
67
|
+
|
|
68
|
+
def update_self_efficacy(self, ase: float, dt: float) -> None:
|
|
69
|
+
# low allostatic self-efficacy accumulates fatigue
|
|
70
|
+
target = clip(1.0 - float(ase), 0.0, 1.0)
|
|
71
|
+
rate = min(1.0, max(0.0, dt) / (6.0 * 3600.0))
|
|
72
|
+
self.fatigue = clip(self.fatigue + (target - self.fatigue) * rate, 0.0, 1.0)
|
|
73
|
+
|
|
74
|
+
def snapshot(self) -> Dict[str, float]:
|
|
75
|
+
return {
|
|
76
|
+
"v": round(self.v, 4),
|
|
77
|
+
"a": round(self.a, 4),
|
|
78
|
+
"dominance": round(self.dominance, 4),
|
|
79
|
+
"stress": round(self.stress, 4),
|
|
80
|
+
"scar": round(self.scar, 4),
|
|
81
|
+
"mood": round(self.mood, 4),
|
|
82
|
+
"fatigue": round(self.fatigue, 3),
|
|
83
|
+
"da": round(self.da, 4),
|
|
84
|
+
"oa": round(self.oa, 4),
|
|
85
|
+
"ht": round(self.ht, 4),
|
|
86
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Biological affect injection and empathy hooks.
|
|
2
|
+
|
|
3
|
+
Emotions can be injected as internal states (not text labels). A user-emotion
|
|
4
|
+
signal can be mirrored with a learnable empathy gain. The base remains the
|
|
5
|
+
source of state; the LLM only expresses it.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Dict, Tuple
|
|
10
|
+
|
|
11
|
+
from .noise import clip
|
|
12
|
+
from .emotion_schema import PAD_PROTOTYPES, pad_for_emotion, emotion_readout
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# prototype affect targets: valence, arousal, stress, oxytocin, cortisol
|
|
16
|
+
EMOTION_PROFILES: Dict[str, Dict[str, float]] = {
|
|
17
|
+
"joy": {"v": 0.8, "a": 0.6, "stress": -0.2, "oxy": 0.2, "cortisol": -0.1},
|
|
18
|
+
"sadness": {"v": -0.7, "a": 0.25, "stress": 0.3, "oxy": -0.1, "cortisol": 0.1},
|
|
19
|
+
"fear": {"v": -0.6, "a": 0.8, "stress": 0.7, "oxy": -0.1, "cortisol": 0.3},
|
|
20
|
+
"anger": {"v": -0.5, "a": 0.75, "stress": 0.6, "oxy": -0.1, "cortisol": 0.2},
|
|
21
|
+
"surprise": {"v": 0.1, "a": 0.8, "stress": 0.2, "oxy": 0.0, "cortisol": 0.0},
|
|
22
|
+
"disgust": {"v": -0.6, "a": 0.5, "stress": 0.4, "oxy": -0.1, "cortisol": 0.1},
|
|
23
|
+
"love": {"v": 0.8, "a": 0.4, "stress": -0.2, "oxy": 0.4, "cortisol": -0.1},
|
|
24
|
+
"loneliness": {"v": -0.5, "a": 0.3, "stress": 0.4, "oxy": -0.2, "cortisol": 0.1},
|
|
25
|
+
"calm": {"v": 0.4, "a": 0.2, "stress": -0.4, "oxy": 0.2, "cortisol": -0.2},
|
|
26
|
+
"anxiety": {"v": -0.4, "a": 0.7, "stress": 0.6, "oxy": -0.1, "cortisol": 0.2},
|
|
27
|
+
"shame": {"v": -0.5, "a": 0.4, "stress": 0.5, "oxy": -0.1, "cortisol": 0.2},
|
|
28
|
+
"guilt": {"v": -0.4, "a": 0.4, "stress": 0.4, "oxy": 0.0, "cortisol": 0.15},
|
|
29
|
+
"pride": {"v": 0.6, "a": 0.5, "stress": -0.1, "oxy": 0.1, "cortisol": 0.0},
|
|
30
|
+
"gratitude": {"v": 0.7, "a": 0.35, "stress": -0.2, "oxy": 0.3, "cortisol": -0.1},
|
|
31
|
+
"hope": {"v": 0.5, "a": 0.45, "stress": -0.1, "oxy": 0.1, "cortisol": 0.0},
|
|
32
|
+
"boredom": {"v": -0.2, "a": 0.15, "stress": 0.1, "oxy": -0.1, "cortisol": 0.0},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def inject_emotion(mind, emotion: str, intensity: float = 0.5, blend: float = 0.6) -> Dict[str, object]:
|
|
37
|
+
"""Inject a biological emotion into affect/physiology, not just a label."""
|
|
38
|
+
name = str(emotion).lower()
|
|
39
|
+
prof = EMOTION_PROFILES.get(name)
|
|
40
|
+
if prof is None:
|
|
41
|
+
return {"injected": None, "emotion": name}
|
|
42
|
+
k = clip(float(intensity), 0.0, 1.0) * clip(float(blend), 0.0, 1.0)
|
|
43
|
+
a = mind.affect
|
|
44
|
+
pad = pad_for_emotion(name)
|
|
45
|
+
a.v = clip(a.v + k * (pad[0] - a.v), -1.0, 1.0)
|
|
46
|
+
a.a = clip(a.a + k * (pad[1] - a.a), 0.0, 1.0)
|
|
47
|
+
if hasattr(a, "dominance"):
|
|
48
|
+
a.dominance = clip(a.dominance + k * (pad[2] - a.dominance), -1.0, 1.0)
|
|
49
|
+
a.stress = clip(a.stress + k * (prof["stress"] - a.stress), 0.0, 1.0)
|
|
50
|
+
try:
|
|
51
|
+
p = mind.physiology
|
|
52
|
+
p.oxytocin = clip(p.oxytocin + k * prof["oxy"], 0.0, 1.0)
|
|
53
|
+
p.cortisol = clip(p.cortisol + k * prof["cortisol"], 0.0, 1.0)
|
|
54
|
+
except Exception:
|
|
55
|
+
pass
|
|
56
|
+
mind.last_injected_emotion = {"emotion": name, "intensity": round(k, 4)}
|
|
57
|
+
return {"injected": name, "intensity": round(k, 4),
|
|
58
|
+
"affect": {"v": round(a.v, 4), "a": round(a.a, 4),
|
|
59
|
+
"dominance": round(float(getattr(a, "dominance", 0.0)), 4),
|
|
60
|
+
"stress": round(a.stress, 4)}}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def empathize(mind, user_emotion: str, intensity: float = 0.5,
|
|
64
|
+
empathy_gain: float = 0.35) -> Dict[str, object]:
|
|
65
|
+
"""Mirror a user emotion with a learnable gain; not a hard rule."""
|
|
66
|
+
return inject_emotion(mind, user_emotion, intensity=float(intensity) * float(empathy_gain), blend=0.7)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def perceive_emotion_lexicon(text: str) -> Tuple[str, float]:
|
|
70
|
+
"""Small deterministic fallback sensor; an LLM/classifier can replace it."""
|
|
71
|
+
t = str(text or "")
|
|
72
|
+
table = [
|
|
73
|
+
("sadness", ("难过", "伤心", "累", "崩溃", "哭", "失落")),
|
|
74
|
+
("anxiety", ("焦虑", "紧张", "担心", "害怕", "慌")),
|
|
75
|
+
("anger", ("生气", "愤怒", "烦", "滚", "讨厌")),
|
|
76
|
+
("joy", ("开心", "高兴", "笑", "太好了", "喜欢")),
|
|
77
|
+
("loneliness", ("孤独", "没人", "一个人", "寂寞")),
|
|
78
|
+
("love", ("爱你", "想你", "抱抱", "亲亲")),
|
|
79
|
+
("gratitude", ("谢谢", "感谢", "麻烦你")),
|
|
80
|
+
]
|
|
81
|
+
for emotion, words in table:
|
|
82
|
+
for w in words:
|
|
83
|
+
if w in t:
|
|
84
|
+
return emotion, 0.6
|
|
85
|
+
return "neutral", 0.0
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def inject_affect(mind, valence: float, arousal: float, dominance: float = 0.0,
|
|
89
|
+
intensity: float = 0.8, blend: float = 0.7) -> Dict[str, object]:
|
|
90
|
+
"""Inject a numeric PAD reading. No categorical label is involved."""
|
|
91
|
+
v = max(-1.0, min(1.0, float(valence)))
|
|
92
|
+
a = max(0.0, min(1.0, float(arousal)))
|
|
93
|
+
d = max(-1.0, min(1.0, float(dominance)))
|
|
94
|
+
k = clip(float(intensity), 0.0, 1.0) * clip(float(blend), 0.0, 1.0)
|
|
95
|
+
# negativity bias: negative events have a stronger pull than positive ones
|
|
96
|
+
k *= 1.25 if v < 0 else 0.90
|
|
97
|
+
aff = mind.affect
|
|
98
|
+
aff.v = clip(aff.v + k * (v - aff.v), -1.0, 1.0)
|
|
99
|
+
aff.a = clip(aff.a + k * (a - aff.a), 0.0, 1.0)
|
|
100
|
+
if hasattr(aff, "dominance"):
|
|
101
|
+
aff.dominance = clip(aff.dominance + k * (d - aff.dominance), -1.0, 1.0)
|
|
102
|
+
aff.stress = clip(aff.stress + k * max(0.0, -v) * 0.45, 0.0, 1.0)
|
|
103
|
+
try:
|
|
104
|
+
p = mind.physiology
|
|
105
|
+
p.oxytocin = clip(p.oxytocin + k * max(0.0, v) * 0.03, 0.0, 1.0)
|
|
106
|
+
p.cortisol = clip(p.cortisol + k * max(0.0, -v) * 0.03, 0.0, 1.0)
|
|
107
|
+
except Exception:
|
|
108
|
+
pass
|
|
109
|
+
return {"valence": round(aff.v, 4), "arousal": round(aff.a, 4),
|
|
110
|
+
"dominance": round(float(getattr(aff, "dominance", 0.0)), 4),
|
|
111
|
+
"stress": round(aff.stress, 4)}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Slow allostatic load: chronic stress accumulates and recovers."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _clip(x, lo=0.0, hi=1.0):
|
|
8
|
+
return max(lo, min(hi, float(x)))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class AllostaticLoad:
|
|
13
|
+
load: float = 0.0
|
|
14
|
+
tau_recovery_s: float = 12 * 3600.0
|
|
15
|
+
|
|
16
|
+
def update(self, dt_s: float, stress: float, fatigue: float,
|
|
17
|
+
sleep_debt: float, sleep_quality: float = 0.5) -> float:
|
|
18
|
+
dt = max(0.0, float(dt_s))
|
|
19
|
+
if dt <= 0:
|
|
20
|
+
return self.load
|
|
21
|
+
drive = 0.5 * _clip(stress) + 0.3 * _clip(fatigue) + 0.2 * _clip(sleep_debt)
|
|
22
|
+
load_up = 0.0025 * max(0.0, drive - 0.25) * (dt / 60.0)
|
|
23
|
+
recover = (1.0 - _clip(sleep_quality)) * (dt / max(1.0, self.tau_recovery_s)) * 0.20
|
|
24
|
+
self.load = _clip(self.load + load_up - recover)
|
|
25
|
+
return round(self.load, 4)
|
|
26
|
+
|
|
27
|
+
def stress_bias(self) -> float:
|
|
28
|
+
return round(0.10 * self.load, 4)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from collections import deque
|
|
5
|
+
from typing import Deque
|
|
6
|
+
|
|
7
|
+
from .config import MindConfig
|
|
8
|
+
from .lexicon import text_affect
|
|
9
|
+
from .noise import clip, jaccard
|
|
10
|
+
from .types import Appraisal, Event
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def occ_emotion(goal_congruence: float, certainty: float, agency: float, coping: float, norm: float) -> str:
|
|
14
|
+
"""OCC-style label; heuristic, not a full OCC implementation."""
|
|
15
|
+
if goal_congruence >= 0.0:
|
|
16
|
+
if agency > 0.25:
|
|
17
|
+
return "pride"
|
|
18
|
+
if agency < -0.25:
|
|
19
|
+
return "gratitude" if coping >= 0.45 else "relief"
|
|
20
|
+
return "joy" if certainty >= 0.55 else "hope"
|
|
21
|
+
if agency < -0.25:
|
|
22
|
+
return "anger" if certainty >= 0.60 else "fear"
|
|
23
|
+
if agency > 0.25:
|
|
24
|
+
return "shame" if norm < 0.45 else "guilt"
|
|
25
|
+
return "distress" if coping < 0.40 else "sadness"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Appraiser:
|
|
29
|
+
"""Turns an event into appraisal: novelty, relevance, control, RPE, threat.
|
|
30
|
+
|
|
31
|
+
This is deliberately a heuristic. The calibration step later replaces
|
|
32
|
+
text_affect() with ANEW / NRC-VAD / language-specific norms.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, cfg: MindConfig, rng: random.Random):
|
|
36
|
+
self.cfg = cfg
|
|
37
|
+
self.rng = rng
|
|
38
|
+
self.expectation = 0.0
|
|
39
|
+
self.recent: Deque[str] = deque(maxlen=24)
|
|
40
|
+
|
|
41
|
+
def _novelty(self, text: str) -> float:
|
|
42
|
+
if not text:
|
|
43
|
+
return 0.3
|
|
44
|
+
now = set(text)
|
|
45
|
+
if not self.recent:
|
|
46
|
+
return 0.55
|
|
47
|
+
best = 0.0
|
|
48
|
+
for old in self.recent:
|
|
49
|
+
best = max(best, jaccard(now, set(old)))
|
|
50
|
+
return clip(1.0 - best, 0.0, 1.0)
|
|
51
|
+
|
|
52
|
+
def appraise(self, ev: Event, state=None, social=None) -> Appraisal:
|
|
53
|
+
v, a = text_affect(ev.text)
|
|
54
|
+
if ev.valence is not None:
|
|
55
|
+
v = clip(ev.valence, -1.0, 1.0)
|
|
56
|
+
if ev.arousal is not None:
|
|
57
|
+
a = clip(ev.arousal, 0.0, 1.0)
|
|
58
|
+
novelty = self._novelty(ev.text)
|
|
59
|
+
relevance = 0.5 if ev.relevance is None else clip(ev.relevance, 0.0, 1.0)
|
|
60
|
+
control = 0.5 if ev.control is None else clip(ev.control, 0.0, 1.0)
|
|
61
|
+
social_sig = 0.0 if ev.social is None else clip(ev.social, -1.0, 1.0)
|
|
62
|
+
rpe = clip(v - self.expectation, -2.0, 2.0)
|
|
63
|
+
threat = clip(max(0.0, -v) * (1.0 - control), 0.0, 1.0)
|
|
64
|
+
goal_relevance = relevance
|
|
65
|
+
goal_congruence = clip(v, -1.0, 1.0)
|
|
66
|
+
if ev.kind == "self_action" or ev.speaker in ("self", "me"):
|
|
67
|
+
agency = 1.0
|
|
68
|
+
elif ev.kind in ("tick", "observation"):
|
|
69
|
+
agency = 0.0
|
|
70
|
+
else:
|
|
71
|
+
agency = -1.0
|
|
72
|
+
certainty = clip(1.0 - 0.6 * novelty, 0.0, 1.0)
|
|
73
|
+
willpower = 0.5
|
|
74
|
+
energy = 0.5
|
|
75
|
+
if state is not None:
|
|
76
|
+
will = getattr(state, "will", None)
|
|
77
|
+
willpower = float(getattr(will, "willpower", 0.5))
|
|
78
|
+
drives = getattr(state, "drives", {})
|
|
79
|
+
energy = float(getattr(drives, "energy", 0.5))
|
|
80
|
+
coping = clip(0.5 * control + 0.3 * willpower + 0.2 * energy, 0.0, 1.0)
|
|
81
|
+
norm = clip(1.0 - 0.5 * threat - 0.3 * max(0.0, -social_sig), 0.0, 1.0)
|
|
82
|
+
self.expectation = 0.85 * self.expectation + 0.15 * v
|
|
83
|
+
if ev.text:
|
|
84
|
+
self.recent.append(ev.text)
|
|
85
|
+
return Appraisal(
|
|
86
|
+
novelty=novelty,
|
|
87
|
+
relevance=relevance,
|
|
88
|
+
control=control,
|
|
89
|
+
rpe=rpe,
|
|
90
|
+
social=social_sig,
|
|
91
|
+
threat=threat,
|
|
92
|
+
valence=v,
|
|
93
|
+
arousal=a,
|
|
94
|
+
goal_relevance=goal_relevance,
|
|
95
|
+
goal_congruence=goal_congruence,
|
|
96
|
+
agency=agency,
|
|
97
|
+
certainty=certainty,
|
|
98
|
+
coping_potential=coping,
|
|
99
|
+
norm_compliance=norm,
|
|
100
|
+
occ_emotion=occ_emotion(goal_congruence, certainty, agency, coping, norm),
|
|
101
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
from .noise import clip
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Attachment:
|
|
9
|
+
"""Two-axis attachment: anxiety (fear of abandonment) and avoidance (distance)."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.trait_anxiety = 0.30
|
|
13
|
+
self.trait_avoidance = 0.30
|
|
14
|
+
self.anxiety = 0.30
|
|
15
|
+
self.avoidance = 0.30
|
|
16
|
+
self.lr = 0.02
|
|
17
|
+
|
|
18
|
+
def update(self, social_signal: float, prediction_error: float, dt: float, fatigue: float = 0.0, scar: float = 0.0) -> None:
|
|
19
|
+
sig = float(social_signal)
|
|
20
|
+
pe = abs(float(prediction_error))
|
|
21
|
+
rate = min(1.0, max(0.0, dt) / 600.0)
|
|
22
|
+
# negative social signal and unpredictable care raise anxiety
|
|
23
|
+
self.anxiety = clip(self.anxiety + self.lr * rate * (max(0.0, -sig) + 0.20 * pe + 0.10 * float(fatigue) + 0.10 * float(scar)), 0.0, 1.0)
|
|
24
|
+
# repeated negative/unpredictable care also raises avoidance
|
|
25
|
+
self.avoidance = clip(self.avoidance + self.lr * rate * (max(0.0, -sig) + 0.10 * pe + 0.08 * float(fatigue) + 0.15 * float(scar)), 0.0, 1.0)
|
|
26
|
+
# positive, predictable care slowly repairs both
|
|
27
|
+
if sig > 0:
|
|
28
|
+
self.anxiety = clip(self.anxiety - self.lr * rate * sig, self.trait_anxiety, 1.0)
|
|
29
|
+
self.avoidance = clip(self.avoidance - self.lr * rate * sig, self.trait_avoidance, 1.0)
|
|
30
|
+
|
|
31
|
+
def action_kind(self) -> str:
|
|
32
|
+
return {"secure": "communicate", "anxious": "seek_help", "avoidant": "avoid", "fearful": "protect"}[self.style()]
|
|
33
|
+
|
|
34
|
+
def memory_bias(self) -> float:
|
|
35
|
+
# anxious attachment prioritizes relational/threat cues
|
|
36
|
+
return 1.0 + 0.30 * float(self.anxiety)
|
|
37
|
+
|
|
38
|
+
def initiation_bias(self) -> float:
|
|
39
|
+
return {"secure": 1.0, "anxious": 1.20, "avoidant": 0.70, "fearful": 0.85}[self.style()]
|
|
40
|
+
|
|
41
|
+
def style(self) -> str:
|
|
42
|
+
a, v = self.anxiety >= 0.55, self.avoidance >= 0.55
|
|
43
|
+
if a and v: return "fearful"
|
|
44
|
+
if a: return "anxious"
|
|
45
|
+
if v: return "avoidant"
|
|
46
|
+
return "secure"
|
|
47
|
+
|
|
48
|
+
def snapshot(self) -> Dict[str, object]:
|
|
49
|
+
return {"anxiety": round(self.anxiety, 3), "avoidance": round(self.avoidance, 3), "style": self.style()}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
from .noise import clip
|
|
6
|
+
from .types import Appraisal, Event
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Attention:
|
|
10
|
+
"""A minimal salience gate: what reaches working memory / focus."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.focus: str | None = None
|
|
14
|
+
self.last_score = 0.0
|
|
15
|
+
|
|
16
|
+
def update(self, ev: Event, ap: Appraisal, drives) -> float:
|
|
17
|
+
score = (
|
|
18
|
+
0.25 * ap.novelty
|
|
19
|
+
+ 0.25 * ap.relevance
|
|
20
|
+
+ 0.20 * ap.threat
|
|
21
|
+
+ 0.20 * abs(ap.valence)
|
|
22
|
+
+ 0.10 * abs(ap.social)
|
|
23
|
+
)
|
|
24
|
+
self.last_score = clip(score, 0.0, 1.0)
|
|
25
|
+
if self.last_score >= 0.45 and ev.speaker:
|
|
26
|
+
self.focus = ev.speaker
|
|
27
|
+
return self.last_score
|
|
28
|
+
|
|
29
|
+
def snapshot(self) -> Dict[str, object]:
|
|
30
|
+
return {"focus": self.focus, "last_score": round(self.last_score, 4)}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import random
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
from .config import MindConfig
|
|
8
|
+
from .noise import clip
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Autonomy:
|
|
12
|
+
"""Endogenous accumulator: no external trigger required to act."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, cfg: MindConfig, rng: random.Random):
|
|
15
|
+
self.cfg = cfg
|
|
16
|
+
self.rng = rng
|
|
17
|
+
self.h = 0.0
|
|
18
|
+
self.last_spoke = -1e9
|
|
19
|
+
self.gain = 1.0
|
|
20
|
+
# learned meta-gain, not a hand-tuned rule
|
|
21
|
+
self.allostatic_gain = 0.05
|
|
22
|
+
self.gain_lr = 0.01
|
|
23
|
+
# learned impulse threshold, initialized from config
|
|
24
|
+
self.threshold = cfg.autonomy_threshold
|
|
25
|
+
self.threshold_lr = 0.002
|
|
26
|
+
self.tau = cfg.autonomy_tau
|
|
27
|
+
self.cooldown = cfg.autonomy_cooldown
|
|
28
|
+
self.tau_lr = 0.005
|
|
29
|
+
self.activity_ema = 0.0
|
|
30
|
+
self.adapt_lr = float(getattr(cfg, "autonomy_adapt_lr", 0.004))
|
|
31
|
+
self.viability_target = float(getattr(cfg, "autonomy_viability_target", 0.90))
|
|
32
|
+
self.social_target = float(getattr(cfg, "autonomy_social_target", 0.50))
|
|
33
|
+
self.activity_target = float(getattr(cfg, "autonomy_activity_target", 0.30))
|
|
34
|
+
|
|
35
|
+
def tick(self, dt: float, clock, drives, mood: float, allostatic: float = 0.0, fatigue: float = 0.0, attachment_bias: float = 1.0) -> None:
|
|
36
|
+
if dt <= 0:
|
|
37
|
+
return
|
|
38
|
+
brake = max(0.2, 1.0 - 0.5 * float(fatigue))
|
|
39
|
+
drive = brake * float(attachment_bias) * self.gain * (clock.drive + 0.20 * drives.get("social") + 0.20 * max(0.0, mood) + self.allostatic_gain * float(allostatic))
|
|
40
|
+
tau = max(self.tau, 1e-6)
|
|
41
|
+
noise = self.rng.gauss(0.0, 0.01) * math.sqrt(dt / tau)
|
|
42
|
+
self.h = self.h * math.exp(-dt / tau) + drive * (dt / tau) + noise
|
|
43
|
+
|
|
44
|
+
def due(self, now: float, asleep: bool) -> bool:
|
|
45
|
+
if asleep:
|
|
46
|
+
return False
|
|
47
|
+
return self.h > self.threshold and (now - self.last_spoke) > self.cooldown
|
|
48
|
+
|
|
49
|
+
def adapt(self, now: float, viability: float, social_need: float, stress: float,
|
|
50
|
+
spoke: bool, dt: float) -> float:
|
|
51
|
+
"""Intrinsic homeostatic adaptation of the impulse threshold. No external labels."""
|
|
52
|
+
if dt <= 0:
|
|
53
|
+
return self.threshold
|
|
54
|
+
spoke_f = 1.0 if spoke else 0.0
|
|
55
|
+
self.activity_ema += 0.02 * (spoke_f - self.activity_ema)
|
|
56
|
+
# low viability -> raise threshold (be conservative); high social need -> lower
|
|
57
|
+
err = (self.viability_target - float(viability)) - 0.5 * (float(social_need) - self.social_target)
|
|
58
|
+
err += 0.15 * (self.activity_target - self.activity_ema)
|
|
59
|
+
rate = self.adapt_lr * min(1.0, max(0.1, float(dt) / 60.0))
|
|
60
|
+
self.threshold = clip(self.threshold + rate * err, 0.20, 2.0)
|
|
61
|
+
return self.threshold
|
|
62
|
+
|
|
63
|
+
def on_speak(self, now: float) -> None:
|
|
64
|
+
self.h *= 0.30
|
|
65
|
+
self.last_spoke = now
|
|
66
|
+
|
|
67
|
+
def learn_from_feedback(self, rpe: float, allostatic: float) -> None:
|
|
68
|
+
# RPE meta-gradient: raise/lower allostatic sensitivity from outcome
|
|
69
|
+
self.allostatic_gain = clip(
|
|
70
|
+
self.allostatic_gain + self.gain_lr * float(rpe) * float(allostatic), 0.0, 0.5
|
|
71
|
+
)
|
|
72
|
+
# outcome learning of the impulse threshold
|
|
73
|
+
self.threshold = clip(
|
|
74
|
+
self.threshold - self.threshold_lr * float(rpe) * min(1.0, abs(self.h)), 0.35, 2.0
|
|
75
|
+
)
|
|
76
|
+
# positive outcome -> faster accumulation / shorter cooldown
|
|
77
|
+
self.tau = clip(self.tau * (1.0 - self.tau_lr * float(rpe)), 60.0, 7200.0)
|
|
78
|
+
self.cooldown = clip(self.cooldown - 20.0 * float(rpe), 10.0, 3600.0)
|
|
79
|
+
|
|
80
|
+
def snapshot(self) -> Dict[str, float]:
|
|
81
|
+
return {"h": round(self.h, 4), "last_spoke": round(self.last_spoke, 1), "allostatic_gain": round(self.allostatic_gain, 4), "threshold": round(self.threshold, 4), "tau": round(self.tau, 1), "cooldown": round(self.cooldown, 1), "viability_target": round(self.viability_target, 4), "social_target": round(self.social_target, 4), "activity_target": round(self.activity_target, 4), "activity_ema": round(self.activity_ema, 4)}
|