dsh-biocore 0.2.0 → 0.3.1
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 +11 -0
- package/lib/core-worker.js +268 -0
- package/lib/index.js +160 -70
- package/lib/modules/autonomy.js +125 -0
- package/lib/modules/cache-stats.js +48 -0
- package/lib/modules/core-turn.js +24 -6
- package/lib/modules/expression-inject.js +0 -10
- package/lib/modules/expression-refresh.js +0 -9
- package/lib/modules/memory-capture.js +2 -1
- package/lib/modules/memory-inject.js +50 -5
- package/lib/modules/memory-tools.js +7 -4
- package/lib/modules/python-env.js +46 -0
- package/lib/modules/safety-gate.js +0 -3
- package/lib/modules/series.js +54 -0
- package/lib/modules/switch.js +16 -0
- package/lib/py-bridge.js +81 -15
- package/package.json +1 -1
- package/python/biocore_memory/core/config.py +138 -127
- package/python/biocore_memory/engine.py +2653 -2476
- package/python/biocore_memory/recall/embedding.py +129 -0
- package/python/biocore_memory/recall/feedback.py +202 -0
- package/python/biocore_model/preference_drift.py +255 -0
- package/python/biocore_model/tests/__init__.py +1 -0
- package/python/biocore_model/tests/core_mode_ui_check.mjs +63 -0
- package/python/biocore_model/tests/core_status_check.mjs +29 -0
- package/python/biocore_model/tests/dom_safe_check.mjs +22 -0
- package/python/biocore_model/tests/leak_samples_run.py +18 -0
- package/python/biocore_model/tests/orb_ring_check.mjs +20 -0
- package/python/biocore_model/tests/panel_html_check.mjs +63 -0
- package/python/biocore_model/tests/persona_flow_check.mjs +17 -0
- package/python/biocore_model/tests/render_guard_check.mjs +19 -0
- package/python/biocore_model/tests/switch_groups_check.mjs +94 -0
- package/python/biocore_model/tests/tabs_state_check.mjs +24 -0
- package/python/biocore_model/tests/test_affective_labor.py +102 -0
- package/python/biocore_model/tests/test_appraisal.py +137 -0
- package/python/biocore_model/tests/test_appraisal_action.py +55 -0
- package/python/biocore_model/tests/test_arch_convergence.py +35 -0
- package/python/biocore_model/tests/test_attachment_behavior.py +95 -0
- package/python/biocore_model/tests/test_attachment_regulation.py +63 -0
- package/python/biocore_model/tests/test_attachment_state.py +29 -0
- package/python/biocore_model/tests/test_attribution.py +56 -0
- package/python/biocore_model/tests/test_attributor.py +108 -0
- package/python/biocore_model/tests/test_autonomic.py +69 -0
- package/python/biocore_model/tests/test_autonomy_accumulator.py +84 -0
- package/python/biocore_model/tests/test_autonomy_affect.py +62 -0
- package/python/biocore_model/tests/test_autonomy_blame.py +44 -0
- package/python/biocore_model/tests/test_autonomy_caps.py +29 -0
- package/python/biocore_model/tests/test_autonomy_check.py +40 -0
- package/python/biocore_model/tests/test_autonomy_clingy.py +41 -0
- package/python/biocore_model/tests/test_autonomy_cooldown_selfcontact.py +138 -0
- package/python/biocore_model/tests/test_autonomy_frustration.py +42 -0
- package/python/biocore_model/tests/test_autonomy_hourly_limit.py +46 -0
- package/python/biocore_model/tests/test_autonomy_loop.py +64 -0
- package/python/biocore_model/tests/test_autonomy_mode.py +37 -0
- package/python/biocore_model/tests/test_autonomy_model.py +60 -0
- package/python/biocore_model/tests/test_autonomy_model_v2.py +68 -0
- package/python/biocore_model/tests/test_autonomy_module_text.py +104 -0
- package/python/biocore_model/tests/test_autonomy_params.py +163 -0
- package/python/biocore_model/tests/test_autonomy_reason.py +34 -0
- package/python/biocore_model/tests/test_autonomy_self.py +153 -0
- package/python/biocore_model/tests/test_autonomy_send_gate.py +38 -0
- package/python/biocore_model/tests/test_autonomy_silence.py +37 -0
- package/python/biocore_model/tests/test_autonomy_status.py +35 -0
- package/python/biocore_model/tests/test_autonomy_test_trigger.py +24 -0
- package/python/biocore_model/tests/test_autonomy_tick.py +148 -0
- package/python/biocore_model/tests/test_autonomy_v4.py +64 -0
- package/python/biocore_model/tests/test_autonomy_v5.py +43 -0
- package/python/biocore_model/tests/test_autonomy_v7.py +48 -0
- package/python/biocore_model/tests/test_autonomy_v_theta.py +85 -0
- package/python/biocore_model/tests/test_batch1_wiring.py +231 -0
- package/python/biocore_model/tests/test_batch2_wiring.py +231 -0
- package/python/biocore_model/tests/test_batch3_wiring.py +223 -0
- package/python/biocore_model/tests/test_bio_tick.py +202 -0
- package/python/biocore_model/tests/test_boundary.py +183 -0
- package/python/biocore_model/tests/test_boundary_gate.py +295 -0
- package/python/biocore_model/tests/test_brain_split.py +99 -0
- package/python/biocore_model/tests/test_co_recall.py +350 -0
- package/python/biocore_model/tests/test_commitment_extractor.py +197 -0
- package/python/biocore_model/tests/test_compassion_fatigue.py +84 -0
- package/python/biocore_model/tests/test_connection_bids.py +69 -0
- package/python/biocore_model/tests/test_core_bridge.py +88 -0
- package/python/biocore_model/tests/test_core_bridge_episode.py +14 -0
- package/python/biocore_model/tests/test_core_bridge_forget.py +17 -0
- package/python/biocore_model/tests/test_core_bridge_identity.py +15 -0
- package/python/biocore_model/tests/test_core_bridge_media.py +17 -0
- package/python/biocore_model/tests/test_core_bridge_relation.py +17 -0
- package/python/biocore_model/tests/test_core_bridge_summary.py +17 -0
- package/python/biocore_model/tests/test_core_client.py +156 -0
- package/python/biocore_model/tests/test_core_commands.py +146 -0
- package/python/biocore_model/tests/test_core_mode.py +161 -0
- package/python/biocore_model/tests/test_core_state.py +240 -0
- package/python/biocore_model/tests/test_criticality.py +61 -0
- package/python/biocore_model/tests/test_cross_session_copy.py +67 -0
- package/python/biocore_model/tests/test_debug_values.py +169 -0
- package/python/biocore_model/tests/test_defense_mechanisms.py +177 -0
- package/python/biocore_model/tests/test_delta_budget.py +157 -0
- package/python/biocore_model/tests/test_diag_trace.py +201 -0
- package/python/biocore_model/tests/test_directives_compat.py +698 -0
- package/python/biocore_model/tests/test_dissonance.py +97 -0
- package/python/biocore_model/tests/test_drift_sign.py +87 -0
- package/python/biocore_model/tests/test_embedding_fallback.py +277 -0
- package/python/biocore_model/tests/test_embedding_local.py +279 -0
- package/python/biocore_model/tests/test_embedding_mode.py +175 -0
- package/python/biocore_model/tests/test_embedding_modes.py +342 -0
- package/python/biocore_model/tests/test_emotion_geometry.py +114 -0
- package/python/biocore_model/tests/test_emotion_probe.py +214 -0
- package/python/biocore_model/tests/test_emotion_probe_concurrency.py +128 -0
- package/python/biocore_model/tests/test_entity_graph.py +198 -0
- package/python/biocore_model/tests/test_entity_resolve.py +446 -0
- package/python/biocore_model/tests/test_envy.py +173 -0
- package/python/biocore_model/tests/test_exit_or_silence.py +209 -0
- package/python/biocore_model/tests/test_expr_guard.py +1 -0
- package/python/biocore_model/tests/test_expr_intent.py +66 -0
- package/python/biocore_model/tests/test_expr_state.py +66 -0
- package/python/biocore_model/tests/test_expr_template.py +71 -0
- package/python/biocore_model/tests/test_expr_tone.py +41 -0
- package/python/biocore_model/tests/test_extreme_scenarios.py +147 -0
- package/python/biocore_model/tests/test_feedback_flow.py +185 -0
- package/python/biocore_model/tests/test_feedback_guard.py +65 -0
- package/python/biocore_model/tests/test_feedback_llm.py +59 -0
- package/python/biocore_model/tests/test_feedback_llm_inferred.py +140 -0
- package/python/biocore_model/tests/test_feedback_memory.py +63 -0
- package/python/biocore_model/tests/test_feedback_user.py +61 -0
- package/python/biocore_model/tests/test_frustration_curve.py +66 -0
- package/python/biocore_model/tests/test_global_bio_state.py +116 -0
- package/python/biocore_model/tests/test_governance.py +181 -0
- package/python/biocore_model/tests/test_granularity.py +133 -0
- package/python/biocore_model/tests/test_graph_extract.py +224 -0
- package/python/biocore_model/tests/test_graph_recall.py +226 -0
- package/python/biocore_model/tests/test_graph_sqlite.py +298 -0
- package/python/biocore_model/tests/test_growth.py +301 -0
- package/python/biocore_model/tests/test_intent_render.py +60 -0
- package/python/biocore_model/tests/test_intero_regulation.py +224 -0
- package/python/biocore_model/tests/test_intero_style.py +146 -0
- package/python/biocore_model/tests/test_item3_numeric.py +105 -0
- package/python/biocore_model/tests/test_item6_numeric.py +116 -0
- package/python/biocore_model/tests/test_layered_memory.py +174 -0
- package/python/biocore_model/tests/test_leak_guard.py +75 -0
- package/python/biocore_model/tests/test_learning_probe.py +93 -0
- package/python/biocore_model/tests/test_life_loop.py +222 -0
- package/python/biocore_model/tests/test_life_loop_10s.py +131 -0
- package/python/biocore_model/tests/test_local_embedding_health.py +258 -0
- package/python/biocore_model/tests/test_memory_affect_encoding.py +79 -0
- package/python/biocore_model/tests/test_memory_backend.py +332 -0
- package/python/biocore_model/tests/test_memory_dedup.py +316 -0
- package/python/biocore_model/tests/test_memory_dual.py +219 -0
- package/python/biocore_model/tests/test_memory_dual_wiring.py +219 -0
- package/python/biocore_model/tests/test_memory_feedback.py +241 -0
- package/python/biocore_model/tests/test_memory_fixes.py +84 -0
- package/python/biocore_model/tests/test_memory_forgetting.py +58 -0
- package/python/biocore_model/tests/test_memory_inject.py +211 -0
- package/python/biocore_model/tests/test_memory_mood_recall.py +118 -0
- package/python/biocore_model/tests/test_memory_store.py +372 -0
- package/python/biocore_model/tests/test_metacog.py +103 -0
- package/python/biocore_model/tests/test_migrate.py +245 -0
- package/python/biocore_model/tests/test_migrate_store.py +194 -0
- package/python/biocore_model/tests/test_mind_wander.py +65 -0
- package/python/biocore_model/tests/test_mood.py +35 -0
- package/python/biocore_model/tests/test_narrative.py +114 -0
- package/python/biocore_model/tests/test_needs_esteem.py +51 -0
- package/python/biocore_model/tests/test_needs_events.py +205 -0
- package/python/biocore_model/tests/test_needs_sleep.py +54 -0
- package/python/biocore_model/tests/test_noise_bank.py +54 -0
- package/python/biocore_model/tests/test_numeric_schema.py +98 -0
- package/python/biocore_model/tests/test_output_protocol.py +65 -0
- package/python/biocore_model/tests/test_page_debug.py +147 -0
- package/python/biocore_model/tests/test_page_status_autonomy.py +215 -0
- package/python/biocore_model/tests/test_pipeline.py +345 -0
- package/python/biocore_model/tests/test_pipeline_api.py +192 -0
- package/python/biocore_model/tests/test_pipeline_mode.py +222 -0
- package/python/biocore_model/tests/test_plugin_config.py +85 -0
- package/python/biocore_model/tests/test_policy_learning.py +116 -0
- package/python/biocore_model/tests/test_possible_selves.py +88 -0
- package/python/biocore_model/tests/test_predictor.py +227 -0
- package/python/biocore_model/tests/test_preference_drift.py +142 -0
- package/python/biocore_model/tests/test_preset.py +142 -0
- package/python/biocore_model/tests/test_proactive_context.py +70 -0
- package/python/biocore_model/tests/test_proactive_history.py +90 -0
- package/python/biocore_model/tests/test_proactive_memory.py +141 -0
- package/python/biocore_model/tests/test_proactive_send_record.py +88 -0
- package/python/biocore_model/tests/test_probe_contract.py +71 -0
- package/python/biocore_model/tests/test_prospective.py +154 -0
- package/python/biocore_model/tests/test_provider_fallback.py +89 -0
- package/python/biocore_model/tests/test_reconstruction.py +111 -0
- package/python/biocore_model/tests/test_regulation_strategies.py +228 -0
- package/python/biocore_model/tests/test_relation_stage.py +114 -0
- package/python/biocore_model/tests/test_relationship_stage.py +59 -0
- package/python/biocore_model/tests/test_release_140.py +55 -0
- package/python/biocore_model/tests/test_repair_after_boundary.py +249 -0
- package/python/biocore_model/tests/test_reply_limit.py +86 -0
- package/python/biocore_model/tests/test_reply_limit_persist.py +48 -0
- package/python/biocore_model/tests/test_reply_split.py +52 -0
- package/python/biocore_model/tests/test_reply_split_astrbot.py +66 -0
- package/python/biocore_model/tests/test_reply_split_inject.py +53 -0
- package/python/biocore_model/tests/test_rerank.py +229 -0
- package/python/biocore_model/tests/test_rollback.py +175 -0
- package/python/biocore_model/tests/test_runtime.py +175 -0
- package/python/biocore_model/tests/test_runtime_active.py +205 -0
- package/python/biocore_model/tests/test_runtime_pipeline.py +1583 -0
- package/python/biocore_model/tests/test_runtime_proactive.py +231 -0
- package/python/biocore_model/tests/test_scheduler.py +79 -0
- package/python/biocore_model/tests/test_secrecy.py +105 -0
- package/python/biocore_model/tests/test_self_esteem.py +45 -0
- package/python/biocore_model/tests/test_self_intent.py +347 -0
- package/python/biocore_model/tests/test_self_interest.py +183 -0
- package/python/biocore_model/tests/test_self_layer_wiring.py +198 -0
- package/python/biocore_model/tests/test_self_verification.py +113 -0
- package/python/biocore_model/tests/test_situation.py +142 -0
- package/python/biocore_model/tests/test_sleep_circadian.py +41 -0
- package/python/biocore_model/tests/test_social_pain.py +49 -0
- package/python/biocore_model/tests/test_social_repair.py +67 -0
- package/python/biocore_model/tests/test_social_theory.py +78 -0
- package/python/biocore_model/tests/test_source_monitoring.py +204 -0
- package/python/biocore_model/tests/test_speak_like_group.py +36 -0
- package/python/biocore_model/tests/test_stages.py +226 -0
- package/python/biocore_model/tests/test_state_tag.py +86 -0
- package/python/biocore_model/tests/test_template_expr.py +65 -0
- package/python/biocore_model/tests/test_text_inference.py +70 -0
- package/python/biocore_model/tests/test_theta_params.py +115 -0
- package/python/biocore_model/tests/test_topic_control.py +200 -0
- package/python/biocore_model/tests/test_turn_memory.py +105 -0
- package/python/biocore_model/tests/test_turn_memory_panel.py +174 -0
- package/python/biocore_model/tests/test_ui_settings.py +84 -0
- package/python/biocore_model/tests/test_ui_settings_authority.py +179 -0
- package/python/biocore_model/tests/test_user_appraisal_async.py +155 -0
- package/python/biocore_model/tests/test_user_classify.py +122 -0
- package/python/biocore_model/tests/test_value_order.py +117 -0
- package/python/biocore_model/tests/test_voice_bank.py +105 -0
- package/python/biocore_model/tests/test_waiting_sense.py +216 -0
- package/python/biocore_model/tests/test_x_directives.py +101 -0
- package/python/biocore_model/tests/ui_compat_check.mjs +22 -0
- package/python/biocore_model/tests/view_mode_check.mjs +17 -0
- package/python/biocore_runtime/autonomy.py +208 -0
- package/python/biocore_runtime/core_state.py +302 -0
- package/python/biocore_runtime/governance.py +495 -0
- package/python/biocore_runtime/growth.py +528 -0
- package/python/biocore_runtime/pipeline.py +850 -23
- package/python/biocore_runtime/relationship_state.py +214 -0
- package/python/biocore_runtime/self_layer.py +103 -0
- package/python/biocore_runtime/service.py +162 -0
- package/python/biocore_runtime/standby.py +472 -0
- package/ui/panel-layout.js +83 -39
- package/ui/panel.css +885 -743
- package/ui/panel.html +356 -296
- package/ui/panel.js +739 -865
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import re, unittest
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from biocore_model import appraisal_action as aa, needs_esteem as ne, needs_sleep as ns
|
|
5
|
+
from biocore_model import attachment_regulation as ar, attachment_state as ast
|
|
6
|
+
from biocore_model import connection_bids as cb, sleep_circadian as sc, memory_forgetting as mf
|
|
7
|
+
|
|
8
|
+
class ConvergenceTest(unittest.TestCase):
|
|
9
|
+
def test_appraisal_delegates(self):
|
|
10
|
+
self.assertEqual(aa.appraisal(0.5, 0.6, 0.7, 0.8, 0.9), aa._bio_appraisal.appraisal(0.5, 0.6, 0.7, 0.8, 0.9))
|
|
11
|
+
def test_appraise_delegates(self):
|
|
12
|
+
self.assertEqual(aa.appraise(-0.5, 0.8, 0.2, 0.6), aa._bio_appraisal.appraise(-0.5, 0.8, 0.2, 0.6))
|
|
13
|
+
def test_needs_esteem_delegates_to_needs_sleep(self):
|
|
14
|
+
old = ne.apply_silence(ne.defaults(), 10)
|
|
15
|
+
new = ns.need_step(ns.defaults(0), {'silence_hours': 10}, dt=0, now=1)
|
|
16
|
+
self.assertAlmostEqual(old['belonging'], new['belonging'], places=3)
|
|
17
|
+
def test_attachment_wrapper_delegates(self):
|
|
18
|
+
wrapped = ar.activation('anxious', threat=0.4, current=0.3, hours=1)
|
|
19
|
+
raised = ast.threat(0.3, 0.4); expected = ast.decay(raised, False, 1)
|
|
20
|
+
self.assertAlmostEqual(wrapped['activation'], expected, places=3)
|
|
21
|
+
def test_trust_derived_from_relationship(self):
|
|
22
|
+
derived = cb.trust_from_relationship({'state': {'trust': 0.7}})
|
|
23
|
+
self.assertAlmostEqual(derived['overall'], 0.7)
|
|
24
|
+
def test_circadian_reads_snapshot(self):
|
|
25
|
+
self.assertAlmostEqual(sc.circadian_from_snapshot({'circadian_alertness': 0.42}), 0.42)
|
|
26
|
+
def test_rumination_single_module_no_core_write(self):
|
|
27
|
+
source = Path(__file__).resolve().parent.parent.joinpath('memory_forgetting.py').read_text(encoding='utf-8')
|
|
28
|
+
self.assertIsNone(re.search(r'core\.[A-Za-z_]+\s*=', source))
|
|
29
|
+
def test_no_core_assignment_in_appraisal(self):
|
|
30
|
+
for name in ('appraisal_action.py', 'bio_appraisal.py'):
|
|
31
|
+
source = Path(__file__).resolve().parent.parent.joinpath(name).read_text(encoding='utf-8')
|
|
32
|
+
self.assertIsNone(re.search(r'core\.[A-Za-z_]+\s*=', source), name)
|
|
33
|
+
|
|
34
|
+
if __name__ == '__main__':
|
|
35
|
+
unittest.main()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Attachment styles as behaviour parameters."""
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from biocore_model import attachment_behavior as ab
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class StyleTest(unittest.TestCase):
|
|
9
|
+
def test_three_styles_distinct(self):
|
|
10
|
+
secure = ab.evaluate("secure")
|
|
11
|
+
anxious = ab.evaluate("anxious")
|
|
12
|
+
avoidant = ab.evaluate("avoidant")
|
|
13
|
+
self.assertGreater(anxious["seeking"], secure["seeking"])
|
|
14
|
+
self.assertGreater(anxious["seeking"], avoidant["seeking"])
|
|
15
|
+
self.assertGreater(avoidant["withdrawal"], secure["withdrawal"])
|
|
16
|
+
self.assertGreater(secure["initiative"], avoidant["initiative"])
|
|
17
|
+
|
|
18
|
+
def test_unknown_style_falls_back_secure(self):
|
|
19
|
+
out = ab.evaluate("weird")
|
|
20
|
+
self.assertEqual(out["style"], "secure")
|
|
21
|
+
|
|
22
|
+
def test_labels(self):
|
|
23
|
+
self.assertEqual(ab.evaluate("anxious")["label"], "焦虑型")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BoundsTest(unittest.TestCase):
|
|
27
|
+
def test_all_params_bounded(self):
|
|
28
|
+
for style in ab.STYLES:
|
|
29
|
+
out = ab.evaluate(style, {"closeness": 1.0, "stress": 1.0})
|
|
30
|
+
for key in ("initiative", "seeking", "withdrawal"):
|
|
31
|
+
self.assertGreaterEqual(out[key], 0.0)
|
|
32
|
+
self.assertLessEqual(out[key], 1.0)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ModifierTest(unittest.TestCase):
|
|
36
|
+
def test_stress_lowers_initiative_raises_withdrawal(self):
|
|
37
|
+
calm = ab.evaluate("secure", {"stress": 0.0})
|
|
38
|
+
tense = ab.evaluate("secure", {"stress": 1.0})
|
|
39
|
+
self.assertGreater(calm["initiative"], tense["initiative"])
|
|
40
|
+
self.assertLess(calm["withdrawal"], tense["withdrawal"])
|
|
41
|
+
|
|
42
|
+
def test_closeness_lifts_initiative(self):
|
|
43
|
+
far = ab.evaluate("secure", {"closeness": 0.1})
|
|
44
|
+
near = ab.evaluate("secure", {"closeness": 0.9})
|
|
45
|
+
self.assertGreater(near["initiative"], far["initiative"])
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class IgnoredTest(unittest.TestCase):
|
|
49
|
+
def test_anxious_seeks_more_when_ignored(self):
|
|
50
|
+
normal = ab.evaluate("anxious")
|
|
51
|
+
ignored = ab.evaluate("anxious", {"ignored": True})
|
|
52
|
+
self.assertGreater(ignored["seeking"], normal["seeking"])
|
|
53
|
+
|
|
54
|
+
def test_avoidant_withdraws_more_when_ignored(self):
|
|
55
|
+
normal = ab.evaluate("avoidant")
|
|
56
|
+
ignored = ab.evaluate("avoidant", {"ignored": True})
|
|
57
|
+
self.assertGreater(ignored["withdrawal"], normal["withdrawal"])
|
|
58
|
+
|
|
59
|
+
def test_on_ignored_text_per_style(self):
|
|
60
|
+
self.assertIn("再发", ab.evaluate("anxious")["on_ignored"])
|
|
61
|
+
self.assertIn("退回去", ab.evaluate("avoidant")["on_ignored"])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class MixedTest(unittest.TestCase):
|
|
65
|
+
def test_blend_sits_between(self):
|
|
66
|
+
blend = ab.evaluate({"secure": 0.5, "anxious": 0.5})
|
|
67
|
+
self.assertTrue(0.4 <= blend["seeking"] <= 0.8)
|
|
68
|
+
|
|
69
|
+
def test_empty_mix_defaults_secure(self):
|
|
70
|
+
self.assertEqual(ab.evaluate({})["style"], "secure")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class FallbackTest(unittest.TestCase):
|
|
74
|
+
def test_disabled(self):
|
|
75
|
+
out = ab.evaluate("anxious", flags={"enabled": False})
|
|
76
|
+
self.assertFalse(out["enabled"])
|
|
77
|
+
self.assertEqual(out["style"], "secure")
|
|
78
|
+
|
|
79
|
+
def test_malformed_context(self):
|
|
80
|
+
out = ab.evaluate("secure", "nope")
|
|
81
|
+
self.assertIn(out["style"], ab.STYLES)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class DirectiveTest(unittest.TestCase):
|
|
85
|
+
def test_withdrawal_shortens(self):
|
|
86
|
+
out = ab.directives(ab.evaluate("avoidant", {"stress": 0.9}))
|
|
87
|
+
self.assertLessEqual(out["length_scale"], 1.0)
|
|
88
|
+
|
|
89
|
+
def test_disabled_directive(self):
|
|
90
|
+
out = ab.directives({"enabled": False})
|
|
91
|
+
self.assertEqual(out["tone"], "")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
unittest.main()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import unittest
|
|
3
|
+
from biocore_model import attachment_regulation as ar
|
|
4
|
+
|
|
5
|
+
class AttachmentTest(unittest.TestCase):
|
|
6
|
+
def test_anxious_drive(self):
|
|
7
|
+
self.assertAlmostEqual(ar.activation('anxious', threat=1.0)['drive_mult'], 1.2)
|
|
8
|
+
def test_avoidant_drive(self):
|
|
9
|
+
self.assertAlmostEqual(ar.activation('avoidant', threat=1.0)['drive_mult'], 0.7)
|
|
10
|
+
def test_secure_mode(self):
|
|
11
|
+
self.assertEqual(ar.activation('secure', threat=1.0)['mode'], 'comfort')
|
|
12
|
+
def test_activation_rises(self):
|
|
13
|
+
self.assertGreater(ar.activation('secure', threat=1.0)['activation'], 0)
|
|
14
|
+
def test_response_drop(self):
|
|
15
|
+
state = ar.activation('secure', threat=0.0, current=0.8, responded=True)
|
|
16
|
+
self.assertAlmostEqual(state['activation'], 0.4)
|
|
17
|
+
def test_decay_per_hour(self):
|
|
18
|
+
state = ar.activation('secure', threat=0.0, current=1.0, hours=5)
|
|
19
|
+
self.assertAlmostEqual(state['activation'], 0.5)
|
|
20
|
+
|
|
21
|
+
class RegulationTest(unittest.TestCase):
|
|
22
|
+
def test_avoidant_suppresses(self):
|
|
23
|
+
self.assertEqual(ar.choose_strategy('avoidant'), 'suppress')
|
|
24
|
+
def test_low_energy_suppresses(self):
|
|
25
|
+
self.assertEqual(ar.choose_strategy('secure', energy=0.2), 'suppress')
|
|
26
|
+
def test_reappraise_mult(self):
|
|
27
|
+
self.assertAlmostEqual(ar.regulate('reappraise')['negative_mult'], 0.6)
|
|
28
|
+
def test_suppress_mult_and_stress(self):
|
|
29
|
+
out = ar.regulate('suppress')
|
|
30
|
+
self.assertAlmostEqual(out['negative_mult'], 0.5); self.assertGreater(out['stress_delta'], 0)
|
|
31
|
+
def test_effort_accumulates(self):
|
|
32
|
+
first = ar.regulate('suppress')['effort']; second = ar.regulate('suppress', effort=first)['effort']
|
|
33
|
+
self.assertGreater(second, first)
|
|
34
|
+
def test_regulation_risk(self):
|
|
35
|
+
self.assertFalse(ar.regulation_risk(0.5)); self.assertTrue(ar.regulation_risk(1.6))
|
|
36
|
+
|
|
37
|
+
class MoodTest(unittest.TestCase):
|
|
38
|
+
def test_mood_toward_emotion(self):
|
|
39
|
+
mood = ar.mood_update({'valence': 0.0, 'arousal': 0.0}, valence=1.0)
|
|
40
|
+
self.assertGreater(mood['valence'], 0); self.assertLess(mood['valence'], 1)
|
|
41
|
+
def test_mood_converges_slowly(self):
|
|
42
|
+
mood = {'valence': 0.0, 'arousal': 0.0}
|
|
43
|
+
for _ in range(10): mood = ar.mood_update(mood, valence=1.0)
|
|
44
|
+
self.assertLess(mood['valence'], 0.5) # 10 ticks ≈ 50 min, still far from target
|
|
45
|
+
def test_mood_half_life_ticks(self):
|
|
46
|
+
ticks = ar.mood_ticks_to_zero(-0.5)
|
|
47
|
+
self.assertGreaterEqual(ticks, 40); self.assertLessEqual(ticks, 80)
|
|
48
|
+
def test_view_fields(self):
|
|
49
|
+
v = ar.view('anxious', 0.5, 'hyperactivating', 'suppress', 0.3, True, {'valence': -0.2, 'arousal': 0.4})
|
|
50
|
+
for key in ('attachment', 'regulation', 'mood'): self.assertIn(key, v)
|
|
51
|
+
|
|
52
|
+
def test_unknown_style_falls_back(self):
|
|
53
|
+
self.assertEqual(ar.activation('bogus')['style'], 'secure')
|
|
54
|
+
|
|
55
|
+
def test_invalid_strategy_falls_back(self):
|
|
56
|
+
self.assertEqual(ar.regulate('bogus')['strategy'], 'reappraise')
|
|
57
|
+
|
|
58
|
+
def test_activation_clamped(self):
|
|
59
|
+
self.assertLessEqual(ar.activation('anxious', threat=5.0)['activation'], 1.0)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == '__main__':
|
|
63
|
+
unittest.main()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import unittest
|
|
3
|
+
from biocore_model import attachment_state as ast
|
|
4
|
+
|
|
5
|
+
class AttachmentStateTest(unittest.TestCase):
|
|
6
|
+
def test_threat_raises(self):
|
|
7
|
+
self.assertGreater(ast.threat(0.0, 1.0), 0.0)
|
|
8
|
+
def test_modes(self):
|
|
9
|
+
self.assertEqual(ast.mode('anxious', 0.5), 'hyperactivating')
|
|
10
|
+
self.assertEqual(ast.mode('secure', 0.5), 'comfort')
|
|
11
|
+
self.assertEqual(ast.mode('avoidant', 0.5), 'deactivating')
|
|
12
|
+
def test_replied_halves(self):
|
|
13
|
+
self.assertAlmostEqual(ast.decay(0.8, True), 0.4)
|
|
14
|
+
def test_no_reply_decays_hourly(self):
|
|
15
|
+
self.assertAlmostEqual(ast.decay(1.0, False, dt=3), 0.7)
|
|
16
|
+
def test_drive_multipliers(self):
|
|
17
|
+
self.assertEqual(ast.drive_multiplier('anxious'), 1.2)
|
|
18
|
+
self.assertEqual(ast.drive_multiplier('avoidant'), 0.7)
|
|
19
|
+
def test_view_fields(self):
|
|
20
|
+
view = ast.view({'activation': 0.4}, 'secure')
|
|
21
|
+
for key in ('style', 'activation', 'mode', 'drive_mult'): self.assertIn(key, view)
|
|
22
|
+
def test_clamp(self):
|
|
23
|
+
self.assertLessEqual(ast.threat(0.95, 1.0), 1.0)
|
|
24
|
+
self.assertGreaterEqual(ast.decay(0.1, False, dt=100), 0.0)
|
|
25
|
+
def test_unknown_style(self):
|
|
26
|
+
self.assertEqual(ast.mode('bogus', 0.5), 'comfort')
|
|
27
|
+
|
|
28
|
+
if __name__ == '__main__':
|
|
29
|
+
unittest.main()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import unittest
|
|
3
|
+
from biocore_model import attribution as at
|
|
4
|
+
|
|
5
|
+
NOW = 1_700_000_000
|
|
6
|
+
|
|
7
|
+
class InferTest(unittest.TestCase):
|
|
8
|
+
def test_shame_quadrant(self):
|
|
9
|
+
a = at.infer({'text': '我总是做不好,没办法改'})
|
|
10
|
+
self.assertEqual(a['locus'], 'internal'); self.assertEqual(a['stability'], 'stable'); self.assertEqual(a['controllability'], 'uncontrollable'); self.assertEqual(a['emotion_hint'], 'shame')
|
|
11
|
+
def test_anger_quadrant(self):
|
|
12
|
+
a = at.infer({'text': '他总是这样,我可以解决'})
|
|
13
|
+
self.assertEqual(a['locus'], 'external'); self.assertEqual(a['emotion_hint'], 'anger')
|
|
14
|
+
def test_sadness_quadrant(self):
|
|
15
|
+
a = at.infer({'text': '老板这次没办法改'})
|
|
16
|
+
self.assertEqual(a['emotion_hint'], 'sadness')
|
|
17
|
+
def test_guilt_quadrant(self):
|
|
18
|
+
a = at.infer({'text': '这次是我的错,可以改'})
|
|
19
|
+
self.assertEqual(a['emotion_hint'], 'guilt')
|
|
20
|
+
def test_neutral_when_ambiguous(self):
|
|
21
|
+
a = at.infer({'text': '今天天气如何'})
|
|
22
|
+
self.assertEqual(a['emotion_hint'], 'neutral'); self.assertEqual(a['locus'], 'neutral')
|
|
23
|
+
def test_conversation_hint_used(self):
|
|
24
|
+
a = at.infer({}, conversation_hint='他总是这样')
|
|
25
|
+
self.assertEqual(a['locus'], 'external')
|
|
26
|
+
|
|
27
|
+
class ApplyTest(unittest.TestCase):
|
|
28
|
+
def test_apply_returns_deltas(self):
|
|
29
|
+
deltas = at.apply({}, {'emotion_hint': 'anger'}, NOW)
|
|
30
|
+
self.assertGreater(deltas['anger'], 0); self.assertGreater(deltas['stress'], 0)
|
|
31
|
+
def test_apply_shame(self):
|
|
32
|
+
deltas = at.apply({}, {'emotion_hint': 'shame'}, NOW)
|
|
33
|
+
self.assertGreater(deltas['self_blame'], 0); self.assertLess(deltas['valence'], 0)
|
|
34
|
+
def test_apply_guilt_repair(self):
|
|
35
|
+
deltas = at.apply({}, {'emotion_hint': 'guilt'}, NOW)
|
|
36
|
+
self.assertGreater(deltas['repair'], 0)
|
|
37
|
+
def test_apply_idempotent_same_now(self):
|
|
38
|
+
first = at.apply({}, {'emotion_hint': 'anger'}, NOW)
|
|
39
|
+
self.assertTrue(at.apply(first, {'emotion_hint': 'anger'}, NOW) == {})
|
|
40
|
+
def test_apply_neutral_empty(self):
|
|
41
|
+
self.assertEqual(at.apply({}, {'emotion_hint': 'neutral'}, NOW), {})
|
|
42
|
+
def test_apply_does_not_touch_appraisal(self):
|
|
43
|
+
deltas = at.apply({}, {'emotion_hint': 'anger'}, NOW)
|
|
44
|
+
self.assertNotIn('goal_congruence', deltas); self.assertNotIn('ego_involvement', deltas)
|
|
45
|
+
|
|
46
|
+
class ViewTest(unittest.TestCase):
|
|
47
|
+
def test_view_fields(self):
|
|
48
|
+
view = at.view({}, at.infer({'text': '老板这次没办法改'}))
|
|
49
|
+
for key in ('locus', 'stability', 'controllability', 'emotion_hint'):
|
|
50
|
+
self.assertIn(key, view)
|
|
51
|
+
def test_view_quadrant_recompute(self):
|
|
52
|
+
view = at.view({}, {'locus': 'external', 'stability': 'stable', 'controllability': 'uncontrollable'})
|
|
53
|
+
self.assertEqual(view['emotion_hint'], 'sadness')
|
|
54
|
+
|
|
55
|
+
if __name__ == '__main__':
|
|
56
|
+
unittest.main()
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Attribution: four reaction types, bounds, fallbacks."""
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from biocore_model import attributor as ab
|
|
6
|
+
|
|
7
|
+
class LocusTest(unittest.TestCase):
|
|
8
|
+
def test_blame_points_at_you(self):
|
|
9
|
+
out = ab.attribute({"blame": 0.9, "goal": -0.5, "hit": 0.6,
|
|
10
|
+
"involve": 0.8}, {"who": "you"})
|
|
11
|
+
self.assertEqual(out["locus"], "you")
|
|
12
|
+
self.assertEqual(out["emotion"], "anger")
|
|
13
|
+
|
|
14
|
+
def test_self_blame_points_at_me(self):
|
|
15
|
+
out = ab.attribute({"blame": 0.5, "goal": -0.6, "hit": 0.8,
|
|
16
|
+
"involve": 0.9}, {"who": "me"})
|
|
17
|
+
self.assertEqual(out["locus"], "me")
|
|
18
|
+
self.assertEqual(out["emotion"], "guilt")
|
|
19
|
+
|
|
20
|
+
def test_low_involvement_points_at_situation(self):
|
|
21
|
+
out = ab.attribute({"blame": 0.05, "goal": -0.2, "hit": 0.2,
|
|
22
|
+
"involve": 0.05}, {"who": "situation"})
|
|
23
|
+
self.assertEqual(out["locus"], "situation")
|
|
24
|
+
|
|
25
|
+
class EmotionTest(unittest.TestCase):
|
|
26
|
+
def test_sympathy_when_other_is_hurt(self):
|
|
27
|
+
out = ab.attribute({"blame": 0.05, "goal": -0.7, "hit": 0.9,
|
|
28
|
+
"involve": 0.2}, {"who": "situation"},
|
|
29
|
+
{"closeness": 0.8})
|
|
30
|
+
self.assertIn(out["emotion"], ("sympathy", "helplessness"))
|
|
31
|
+
|
|
32
|
+
def test_uncontrollable_self_is_helplessness(self):
|
|
33
|
+
out = ab.attribute({"blame": 0.0, "goal": -0.5, "hit": 0.3,
|
|
34
|
+
"involve": 0.1}, {"who": "me"})
|
|
35
|
+
self.assertIn(out["emotion"], ("helplessness", "guilt"))
|
|
36
|
+
|
|
37
|
+
class BoundsTest(unittest.TestCase):
|
|
38
|
+
def test_scores_in_range(self):
|
|
39
|
+
out = ab.attribute({"blame": 5, "goal": -9, "hit": 3,
|
|
40
|
+
"involve": -2})
|
|
41
|
+
for value in out["scores"].values():
|
|
42
|
+
self.assertGreaterEqual(value, 0.0)
|
|
43
|
+
self.assertLessEqual(value, 1.0)
|
|
44
|
+
self.assertIn(out["locus"], ab.LOCI)
|
|
45
|
+
self.assertIn(out["emotion"], ab.EMOTIONS)
|
|
46
|
+
self.assertGreaterEqual(out["stability"], 0.0)
|
|
47
|
+
self.assertLessEqual(out["stability"], 1.0)
|
|
48
|
+
|
|
49
|
+
def test_confidence_not_negative(self):
|
|
50
|
+
out = ab.attribute({})
|
|
51
|
+
self.assertGreaterEqual(out["confidence"], 0.0)
|
|
52
|
+
|
|
53
|
+
class StabilityTest(unittest.TestCase):
|
|
54
|
+
def test_conflict_raises_stability(self):
|
|
55
|
+
low = ab.attribute({"blame": 0.4}, {}, {"conflict": 0.0})
|
|
56
|
+
high = ab.attribute({"blame": 0.4}, {}, {"conflict": 0.9})
|
|
57
|
+
self.assertGreater(high["stability"], low["stability"])
|
|
58
|
+
|
|
59
|
+
class ControllabilityTest(unittest.TestCase):
|
|
60
|
+
def test_situation_is_less_controllable(self):
|
|
61
|
+
a = ab.attribute({"blame": 0.6, "involve": 0.6}, {"who": "situation"})
|
|
62
|
+
b = ab.attribute({"blame": 0.6, "involve": 0.6}, {"who": "you"})
|
|
63
|
+
self.assertLess(a["controllability"], b["controllability"])
|
|
64
|
+
|
|
65
|
+
class FallbackTest(unittest.TestCase):
|
|
66
|
+
def test_disabled_is_neutral(self):
|
|
67
|
+
out = ab.attribute({"blame": 0.9}, {"who": "you"},
|
|
68
|
+
flags={"enabled": False})
|
|
69
|
+
self.assertFalse(out["enabled"])
|
|
70
|
+
self.assertEqual(out["emotion"], "neutral")
|
|
71
|
+
self.assertEqual(out["locus"], "situation")
|
|
72
|
+
|
|
73
|
+
def test_missing_fields_safe(self):
|
|
74
|
+
out = ab.attribute(None, None, None)
|
|
75
|
+
self.assertTrue(out["attribution_reason"])
|
|
76
|
+
self.assertTrue(out["reaction"])
|
|
77
|
+
|
|
78
|
+
def test_malformed_dims(self):
|
|
79
|
+
out = ab.attribute({"blame": "x", "goal": float("nan")})
|
|
80
|
+
self.assertIn(out["locus"], ab.LOCI)
|
|
81
|
+
|
|
82
|
+
class DirectiveTest(unittest.TestCase):
|
|
83
|
+
def test_each_emotion_maps(self):
|
|
84
|
+
cases = {"anger": "angry", "guilt": "guilty",
|
|
85
|
+
"helplessness": "helpless", "sympathy": "kind"}
|
|
86
|
+
for emotion in cases:
|
|
87
|
+
out = ab.directives({"enabled": True, "emotion": emotion,
|
|
88
|
+
"confidence": 0.5})
|
|
89
|
+
self.assertEqual(out["emotion"], emotion)
|
|
90
|
+
self.assertGreaterEqual(out["length_scale"], 0.5)
|
|
91
|
+
|
|
92
|
+
def test_disabled_directive(self):
|
|
93
|
+
out = ab.directives({"enabled": False})
|
|
94
|
+
self.assertEqual(out["tone"], "")
|
|
95
|
+
|
|
96
|
+
class ThreeReactionsTest(unittest.TestCase):
|
|
97
|
+
def test_same_event_three_loci(self):
|
|
98
|
+
event = {"text": "他失约了"}
|
|
99
|
+
a = ab.attribute({"blame": 0.9, "goal": -0.6, "hit": 0.5,
|
|
100
|
+
"involve": 0.8}, dict(event, who="you"))
|
|
101
|
+
b = ab.attribute({"blame": 0.5, "goal": -0.6, "hit": 0.8,
|
|
102
|
+
"involve": 0.9}, dict(event, who="me"))
|
|
103
|
+
c = ab.attribute({"blame": 0.1, "goal": -0.6, "hit": 0.5,
|
|
104
|
+
"involve": 0.1}, dict(event, who="situation"))
|
|
105
|
+
self.assertEqual(len({a["emotion"], b["emotion"], c["emotion"]}), 3)
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
unittest.main()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""HUMAN-3 autonomic oscillation tests: band, period, threshold modulation."""
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from biocore_model import autonomic as au
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FrequencyTest(unittest.TestCase):
|
|
9
|
+
def test_default_and_none(self):
|
|
10
|
+
self.assertEqual(au.frequency(None), au.DEFAULT_FREQ)
|
|
11
|
+
self.assertEqual(au.frequency(), au.DEFAULT_FREQ)
|
|
12
|
+
|
|
13
|
+
def test_clamped_to_band(self):
|
|
14
|
+
self.assertEqual(au.frequency(0.01), au.FREQ_MIN)
|
|
15
|
+
self.assertEqual(au.frequency(5.0), au.FREQ_MAX)
|
|
16
|
+
|
|
17
|
+
def test_period(self):
|
|
18
|
+
self.assertAlmostEqual(au.period(0.2), 5.0, places=4)
|
|
19
|
+
self.assertAlmostEqual(au.period(0.1), 10.0, places=4)
|
|
20
|
+
|
|
21
|
+
def test_in_band(self):
|
|
22
|
+
self.assertTrue(au.in_band(0.2))
|
|
23
|
+
self.assertFalse(au.in_band(0.05))
|
|
24
|
+
self.assertFalse(au.in_band("x"))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class WaveTest(unittest.TestCase):
|
|
28
|
+
def test_amplitude_bound_and_zero(self):
|
|
29
|
+
self.assertAlmostEqual(au.wave(0.0), 0.0, places=4)
|
|
30
|
+
for now in range(0, 20):
|
|
31
|
+
self.assertLessEqual(abs(au.wave(now * 0.37, amplitude=0.4)), 0.4)
|
|
32
|
+
|
|
33
|
+
def test_periodic(self):
|
|
34
|
+
self.assertAlmostEqual(au.wave(1.0, freq=0.2),
|
|
35
|
+
au.wave(1.0 + au.period(0.2), freq=0.2), places=3)
|
|
36
|
+
|
|
37
|
+
def test_phase_shift_equals_time_shift(self):
|
|
38
|
+
self.assertAlmostEqual(au.wave(0.0, phase0=1.0),
|
|
39
|
+
au.wave(1.0 / (2 * 3.141592653589793 * 0.2)), places=3)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ThresholdTest(unittest.TestCase):
|
|
43
|
+
def test_threshold_bounds(self):
|
|
44
|
+
for now in range(0, 30):
|
|
45
|
+
value = au.threshold(now=now * 0.4)
|
|
46
|
+
self.assertGreaterEqual(value, au.THRESHOLD_MIN)
|
|
47
|
+
self.assertLessEqual(value, au.THRESHOLD_MAX)
|
|
48
|
+
|
|
49
|
+
def test_threshold_oscillates_around_base(self):
|
|
50
|
+
values = [au.threshold(now=index * 0.5) for index in range(20)]
|
|
51
|
+
self.assertLess(min(values), au.BASE_THRESHOLD)
|
|
52
|
+
self.assertGreater(max(values), au.BASE_THRESHOLD)
|
|
53
|
+
|
|
54
|
+
def test_zero_amplitude_constant(self):
|
|
55
|
+
self.assertEqual(au.threshold(now=3.0, amplitude=0.0), au.BASE_THRESHOLD)
|
|
56
|
+
|
|
57
|
+
def test_modulate_triggered(self):
|
|
58
|
+
low = au.modulate(0.0, now=1.0)
|
|
59
|
+
high = au.modulate(0.99, now=1.0)
|
|
60
|
+
self.assertFalse(low["triggered"])
|
|
61
|
+
self.assertTrue(high["triggered"])
|
|
62
|
+
self.assertGreater(high["ratio"], 1.0)
|
|
63
|
+
|
|
64
|
+
def test_sample_length(self):
|
|
65
|
+
self.assertEqual(len(au.sample(0.0, seconds=5.0, step=0.5)), 10)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
unittest.main()
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import math, random, unittest
|
|
3
|
+
from biocore_model import autonomy_model as am
|
|
4
|
+
|
|
5
|
+
class AccumulatorTest(unittest.TestCase):
|
|
6
|
+
def test_v_accumulates_until_theta(self):
|
|
7
|
+
V = 0.0; fired = 0
|
|
8
|
+
for _ in range(30):
|
|
9
|
+
out = am.step(V, 0.1, leak=0.15, sigma=0.0, theta=0.55)
|
|
10
|
+
V = out['V']
|
|
11
|
+
if out['fire']: fired += 1; self.assertEqual(V, 0.0)
|
|
12
|
+
self.assertGreaterEqual(fired, 1)
|
|
13
|
+
|
|
14
|
+
def test_leak_half_life_about_4_3_ticks(self):
|
|
15
|
+
V = 1.0
|
|
16
|
+
for _ in range(5):
|
|
17
|
+
V = am.step(V, 0.0, leak=0.15, sigma=0.0, theta=99)['V']
|
|
18
|
+
self.assertAlmostEqual(V, 0.85 ** 5, places=6)
|
|
19
|
+
half = math.log(0.5) / math.log(1 - 0.15)
|
|
20
|
+
self.assertAlmostEqual(half, 4.27, delta=0.05)
|
|
21
|
+
|
|
22
|
+
def test_sigma_zero_deterministic(self):
|
|
23
|
+
a = [am.step(0.0, 0.1, sigma=0.0)['V'] for _ in range(3)]
|
|
24
|
+
b = [am.step(0.0, 0.1, sigma=0.0)['V'] for _ in range(3)]
|
|
25
|
+
self.assertEqual(a, b)
|
|
26
|
+
|
|
27
|
+
def test_sigma_creates_jitter(self):
|
|
28
|
+
rng = random.Random(42)
|
|
29
|
+
times = []
|
|
30
|
+
for _ in range(20):
|
|
31
|
+
V = 0.0
|
|
32
|
+
for tick in range(1, 200):
|
|
33
|
+
out = am.step(V, 0.1, sigma=0.06, theta=0.55, rng=rng)
|
|
34
|
+
V = out['V']
|
|
35
|
+
if out['fire']: times.append(tick); break
|
|
36
|
+
self.assertGreater(len(set(times)), 1)
|
|
37
|
+
|
|
38
|
+
def test_fire_resets_v(self):
|
|
39
|
+
out = am.step(0.6, 0.1, sigma=0.0, theta=0.55)
|
|
40
|
+
self.assertTrue(out['fire']); self.assertEqual(out['V'], 0.0)
|
|
41
|
+
|
|
42
|
+
def test_user_reply_satiety(self):
|
|
43
|
+
self.assertAlmostEqual(am.on_user_reply(1.0), 0.3)
|
|
44
|
+
|
|
45
|
+
def test_drift_weights_sum(self):
|
|
46
|
+
self.assertAlmostEqual(sum(am.DRIFT_WEIGHTS.values()), 1.0, places=6)
|
|
47
|
+
|
|
48
|
+
def test_drift_value_clamped(self):
|
|
49
|
+
self.assertLessEqual(am.drift_value({k: 1.0 for k in am.DRIFT_WEIGHTS}), 1.0)
|
|
50
|
+
|
|
51
|
+
def test_memory_due_today(self):
|
|
52
|
+
now = 1_700_000_000
|
|
53
|
+
out = am.memory_due([{'id': 'e1', 'due_ts': now + 3600}], now)
|
|
54
|
+
self.assertAlmostEqual(out['memory_due'], 1.0)
|
|
55
|
+
|
|
56
|
+
def test_memory_due_commitment_bonus(self):
|
|
57
|
+
now = 1_700_000_000
|
|
58
|
+
out = am.memory_due([{'id': 'e1', 'due_ts': now + 86400 * 1.2, 'title': '记得答应她'}], now)
|
|
59
|
+
self.assertGreater(out['memory_due'], 0.7)
|
|
60
|
+
|
|
61
|
+
def test_memory_due_only_once(self):
|
|
62
|
+
now = 1_700_000_000
|
|
63
|
+
first = am.memory_due([{'id': 'e1', 'due_ts': now + 3600}], now)
|
|
64
|
+
second = am.memory_due([{'id': 'e1', 'due_ts': now + 3600}], now, triggered={'e1': now})
|
|
65
|
+
self.assertEqual(first['memory_due'], 1.0); self.assertEqual(second['memory_due'], 0.0)
|
|
66
|
+
|
|
67
|
+
def test_memory_due_far_events_ignored(self):
|
|
68
|
+
now = 1_700_000_000
|
|
69
|
+
self.assertEqual(am.memory_due([{'id': 'x', 'due_ts': now + 86400 * 10}], now)['memory_due'], 0.0)
|
|
70
|
+
|
|
71
|
+
def test_snapshot_fields(self):
|
|
72
|
+
snap = am.accumulator_snapshot({'V': 0.3, 'ticks_since_fire': 4}, theta=0.55)
|
|
73
|
+
for key in ('V', 'theta', 'drift', 'leak', 'sigma', 'ticks_since_fire', 'to_threshold'):
|
|
74
|
+
self.assertIn(key, snap)
|
|
75
|
+
|
|
76
|
+
def test_to_threshold(self):
|
|
77
|
+
snap = am.accumulator_snapshot({'V': 0.25}, theta=0.55)
|
|
78
|
+
self.assertAlmostEqual(snap['to_threshold'], 0.3)
|
|
79
|
+
|
|
80
|
+
def test_theta_configurable(self):
|
|
81
|
+
self.assertFalse(am.step(0.4, 0.05, theta=0.9, sigma=0.0)['fire'])
|
|
82
|
+
|
|
83
|
+
if __name__ == '__main__':
|
|
84
|
+
unittest.main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import unittest
|
|
3
|
+
from biocore_model import autonomy_affect as aa
|
|
4
|
+
|
|
5
|
+
class FakeCore:
|
|
6
|
+
v = 0.0; a = 0.5; stress = 0.2; social = 0.5; confidence = 1.0
|
|
7
|
+
|
|
8
|
+
NOW = 1_700_000_000
|
|
9
|
+
|
|
10
|
+
class AffectTest(unittest.TestCase):
|
|
11
|
+
def test_silence_deltas(self):
|
|
12
|
+
delta = aa.compute_deltas(24, 1.0, 0.0)
|
|
13
|
+
self.assertAlmostEqual(delta['valence'], -0.015); self.assertAlmostEqual(delta['lonely'], 0.02)
|
|
14
|
+
def test_blame_valence(self):
|
|
15
|
+
delta = aa.compute_deltas(0, 0.0, 0.5)
|
|
16
|
+
self.assertAlmostEqual(delta['valence'], -0.05); self.assertAlmostEqual(delta['confidence'], 0.75)
|
|
17
|
+
def test_dt_cap(self):
|
|
18
|
+
capped = aa.compute_deltas(100, 1.0, 0.0)
|
|
19
|
+
full = aa.compute_deltas(6, 1.0, 0.0)
|
|
20
|
+
self.assertAlmostEqual(capped['valence'], full['valence'])
|
|
21
|
+
def test_apply_applies_once(self):
|
|
22
|
+
core = FakeCore()
|
|
23
|
+
_, acc = aa.apply(core, {}, NOW, 24, 1.0, 0.0)
|
|
24
|
+
v1 = core.v
|
|
25
|
+
_, acc = aa.apply(core, acc, NOW, 24, 1.0, 0.0)
|
|
26
|
+
self.assertAlmostEqual(core.v, v1)
|
|
27
|
+
def test_apply_clamps(self):
|
|
28
|
+
core = FakeCore(); core.v = -0.55
|
|
29
|
+
aa.apply(core, {}, NOW, 100, 4.0, 0.5)
|
|
30
|
+
self.assertGreaterEqual(core.v, -0.6)
|
|
31
|
+
def test_confidence_written(self):
|
|
32
|
+
core = FakeCore()
|
|
33
|
+
aa.apply(core, {}, NOW, 0, 0.0, 0.6)
|
|
34
|
+
self.assertAlmostEqual(core.confidence, 0.7)
|
|
35
|
+
def test_affect_field_on_core(self):
|
|
36
|
+
core = FakeCore()
|
|
37
|
+
aa.apply(core, {}, NOW, 12, 0.5, 0.2)
|
|
38
|
+
self.assertIn('autonomy_affect', core.__dict__)
|
|
39
|
+
def test_affect_in_acc(self):
|
|
40
|
+
_, acc = aa.apply(FakeCore(), {}, NOW, 12, 0.5, 0.0)
|
|
41
|
+
for key in ('valence', 'arousal', 'stress', 'lonely'):
|
|
42
|
+
self.assertIn(key, acc['affect'])
|
|
43
|
+
def test_no_duplicate_stress(self):
|
|
44
|
+
delta = aa.compute_deltas(24, 1.0, 0.0)
|
|
45
|
+
self.assertEqual(delta['stress'], 0.0); self.assertEqual(delta['arousal'], 0.0)
|
|
46
|
+
def test_first_tick_dt_zero(self):
|
|
47
|
+
core = FakeCore()
|
|
48
|
+
delta, _ = aa.apply(core, {}, NOW, 24, 1.0, 0.0)
|
|
49
|
+
self.assertEqual(delta['valence'], 0.0)
|
|
50
|
+
def test_lonely_only_from_silence(self):
|
|
51
|
+
delta = aa.compute_deltas(0, 0.0, 1.0)
|
|
52
|
+
self.assertEqual(delta['lonely'], 0.0)
|
|
53
|
+
def test_second_call_same_now_zero(self):
|
|
54
|
+
core = FakeCore()
|
|
55
|
+
_, acc = aa.apply(core, {}, NOW - 3600, 12, 1.0, 0.0)
|
|
56
|
+
_, acc = aa.apply(core, acc, NOW, 12, 1.0, 0.0)
|
|
57
|
+
v = core.v
|
|
58
|
+
_, acc = aa.apply(core, acc, NOW, 12, 1.0, 0.0)
|
|
59
|
+
self.assertAlmostEqual(core.v, v)
|
|
60
|
+
|
|
61
|
+
if __name__ == '__main__':
|
|
62
|
+
unittest.main()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import unittest
|
|
3
|
+
from biocore_model import autonomy_model as am, proactive_context as pc
|
|
4
|
+
|
|
5
|
+
class BlameTest(unittest.TestCase):
|
|
6
|
+
def test_6h_low(self):
|
|
7
|
+
self.assertLess(am.self_blame_value(6, 0.4, 'secure'), 0.12)
|
|
8
|
+
def test_24h(self):
|
|
9
|
+
self.assertAlmostEqual(am.self_blame_value(24, 0.4, 'secure'), 0.4)
|
|
10
|
+
def test_48h_capped(self):
|
|
11
|
+
self.assertAlmostEqual(am.self_blame_value(48, 0.4, 'secure'), 0.7)
|
|
12
|
+
def test_rs_scales(self):
|
|
13
|
+
self.assertAlmostEqual(am.self_blame_value(24, 0.8, 'secure'), 0.7)
|
|
14
|
+
def test_anxious(self):
|
|
15
|
+
self.assertAlmostEqual(am.self_blame_value(24, 0.4, 'anxious'), 0.52)
|
|
16
|
+
def test_avoidant(self):
|
|
17
|
+
self.assertAlmostEqual(am.self_blame_value(24, 0.4, 'avoidant'), 0.2)
|
|
18
|
+
def test_decay(self):
|
|
19
|
+
self.assertAlmostEqual(am.self_blame_decay(0.4, 2), 0.2)
|
|
20
|
+
self.assertEqual(am.self_blame_decay(0.4, 5), 0.0)
|
|
21
|
+
def test_blame_mult(self):
|
|
22
|
+
self.assertAlmostEqual(am.blame_mult('anxious', 0.5), 1.075)
|
|
23
|
+
self.assertAlmostEqual(am.blame_mult('avoidant', 0.5), 0.85)
|
|
24
|
+
self.assertAlmostEqual(am.blame_mult('secure', 0.5), 1.0)
|
|
25
|
+
def test_blame_effects(self):
|
|
26
|
+
eff = am.blame_effects(0.4)
|
|
27
|
+
self.assertAlmostEqual(eff['valence'], -0.04); self.assertAlmostEqual(eff['confidence'], 0.8)
|
|
28
|
+
self.assertNotIn('stress', eff); self.assertNotIn('arousal', eff)
|
|
29
|
+
def test_motive_fields(self):
|
|
30
|
+
view = am.motive_view(24, 2, attachment_style='anxious', self_blame=0.5)
|
|
31
|
+
for key in ('silence_mult', 'frust_mult', 'blame_mult', 'total_mult', 'confidence'):
|
|
32
|
+
self.assertIn(key, view)
|
|
33
|
+
def test_clamp_range(self):
|
|
34
|
+
self.assertLessEqual(am.compose_mult(2.0, 10, 1.5), 1.8)
|
|
35
|
+
self.assertGreaterEqual(am.compose_mult(0.2, 0, 0.5), 0.2)
|
|
36
|
+
def test_confidence(self):
|
|
37
|
+
self.assertAlmostEqual(am.motive_view(0, 0, self_blame=0.4)['confidence'], 0.8)
|
|
38
|
+
def test_tone_lines(self):
|
|
39
|
+
self.assertEqual(len(pc.tone_lines(0.2)), 0)
|
|
40
|
+
self.assertEqual(len(pc.tone_lines(0.4)), 1)
|
|
41
|
+
self.assertEqual(len(pc.tone_lines(0.6)), 2)
|
|
42
|
+
|
|
43
|
+
if __name__ == '__main__':
|
|
44
|
+
unittest.main()
|