dsh-biocore 0.2.0 → 0.3.0

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.
Files changed (245) hide show
  1. package/README.md +11 -0
  2. package/lib/core-worker.js +268 -0
  3. package/lib/index.js +160 -70
  4. package/lib/modules/autonomy.js +125 -0
  5. package/lib/modules/cache-stats.js +48 -0
  6. package/lib/modules/core-turn.js +24 -6
  7. package/lib/modules/expression-inject.js +0 -10
  8. package/lib/modules/expression-refresh.js +0 -9
  9. package/lib/modules/memory-capture.js +2 -1
  10. package/lib/modules/memory-inject.js +50 -5
  11. package/lib/modules/memory-tools.js +7 -4
  12. package/lib/modules/python-env.js +46 -0
  13. package/lib/modules/safety-gate.js +0 -3
  14. package/lib/modules/series.js +54 -0
  15. package/lib/modules/switch.js +16 -0
  16. package/lib/py-bridge.js +81 -15
  17. package/package.json +1 -1
  18. package/python/biocore_memory/core/config.py +138 -127
  19. package/python/biocore_memory/engine.py +2653 -2476
  20. package/python/biocore_memory/recall/embedding.py +129 -0
  21. package/python/biocore_memory/recall/feedback.py +202 -0
  22. package/python/biocore_model/preference_drift.py +255 -0
  23. package/python/biocore_model/tests/__init__.py +1 -0
  24. package/python/biocore_model/tests/core_mode_ui_check.mjs +63 -0
  25. package/python/biocore_model/tests/core_status_check.mjs +29 -0
  26. package/python/biocore_model/tests/dom_safe_check.mjs +22 -0
  27. package/python/biocore_model/tests/leak_samples_run.py +18 -0
  28. package/python/biocore_model/tests/orb_ring_check.mjs +20 -0
  29. package/python/biocore_model/tests/panel_html_check.mjs +63 -0
  30. package/python/biocore_model/tests/persona_flow_check.mjs +17 -0
  31. package/python/biocore_model/tests/render_guard_check.mjs +19 -0
  32. package/python/biocore_model/tests/switch_groups_check.mjs +94 -0
  33. package/python/biocore_model/tests/tabs_state_check.mjs +24 -0
  34. package/python/biocore_model/tests/test_affective_labor.py +102 -0
  35. package/python/biocore_model/tests/test_appraisal.py +137 -0
  36. package/python/biocore_model/tests/test_appraisal_action.py +55 -0
  37. package/python/biocore_model/tests/test_arch_convergence.py +35 -0
  38. package/python/biocore_model/tests/test_attachment_behavior.py +95 -0
  39. package/python/biocore_model/tests/test_attachment_regulation.py +63 -0
  40. package/python/biocore_model/tests/test_attachment_state.py +29 -0
  41. package/python/biocore_model/tests/test_attribution.py +56 -0
  42. package/python/biocore_model/tests/test_attributor.py +108 -0
  43. package/python/biocore_model/tests/test_autonomic.py +69 -0
  44. package/python/biocore_model/tests/test_autonomy_accumulator.py +84 -0
  45. package/python/biocore_model/tests/test_autonomy_affect.py +62 -0
  46. package/python/biocore_model/tests/test_autonomy_blame.py +44 -0
  47. package/python/biocore_model/tests/test_autonomy_caps.py +29 -0
  48. package/python/biocore_model/tests/test_autonomy_check.py +40 -0
  49. package/python/biocore_model/tests/test_autonomy_clingy.py +41 -0
  50. package/python/biocore_model/tests/test_autonomy_cooldown_selfcontact.py +138 -0
  51. package/python/biocore_model/tests/test_autonomy_frustration.py +42 -0
  52. package/python/biocore_model/tests/test_autonomy_hourly_limit.py +46 -0
  53. package/python/biocore_model/tests/test_autonomy_loop.py +64 -0
  54. package/python/biocore_model/tests/test_autonomy_mode.py +37 -0
  55. package/python/biocore_model/tests/test_autonomy_model.py +60 -0
  56. package/python/biocore_model/tests/test_autonomy_model_v2.py +68 -0
  57. package/python/biocore_model/tests/test_autonomy_module_text.py +104 -0
  58. package/python/biocore_model/tests/test_autonomy_params.py +163 -0
  59. package/python/biocore_model/tests/test_autonomy_reason.py +34 -0
  60. package/python/biocore_model/tests/test_autonomy_self.py +153 -0
  61. package/python/biocore_model/tests/test_autonomy_send_gate.py +38 -0
  62. package/python/biocore_model/tests/test_autonomy_silence.py +37 -0
  63. package/python/biocore_model/tests/test_autonomy_status.py +35 -0
  64. package/python/biocore_model/tests/test_autonomy_test_trigger.py +24 -0
  65. package/python/biocore_model/tests/test_autonomy_tick.py +148 -0
  66. package/python/biocore_model/tests/test_autonomy_v4.py +64 -0
  67. package/python/biocore_model/tests/test_autonomy_v5.py +43 -0
  68. package/python/biocore_model/tests/test_autonomy_v7.py +48 -0
  69. package/python/biocore_model/tests/test_autonomy_v_theta.py +85 -0
  70. package/python/biocore_model/tests/test_batch1_wiring.py +231 -0
  71. package/python/biocore_model/tests/test_batch2_wiring.py +231 -0
  72. package/python/biocore_model/tests/test_batch3_wiring.py +223 -0
  73. package/python/biocore_model/tests/test_bio_tick.py +202 -0
  74. package/python/biocore_model/tests/test_boundary.py +183 -0
  75. package/python/biocore_model/tests/test_boundary_gate.py +295 -0
  76. package/python/biocore_model/tests/test_brain_split.py +99 -0
  77. package/python/biocore_model/tests/test_co_recall.py +350 -0
  78. package/python/biocore_model/tests/test_commitment_extractor.py +197 -0
  79. package/python/biocore_model/tests/test_compassion_fatigue.py +84 -0
  80. package/python/biocore_model/tests/test_connection_bids.py +69 -0
  81. package/python/biocore_model/tests/test_core_bridge.py +88 -0
  82. package/python/biocore_model/tests/test_core_bridge_episode.py +14 -0
  83. package/python/biocore_model/tests/test_core_bridge_forget.py +17 -0
  84. package/python/biocore_model/tests/test_core_bridge_identity.py +15 -0
  85. package/python/biocore_model/tests/test_core_bridge_media.py +17 -0
  86. package/python/biocore_model/tests/test_core_bridge_relation.py +17 -0
  87. package/python/biocore_model/tests/test_core_bridge_summary.py +17 -0
  88. package/python/biocore_model/tests/test_core_client.py +156 -0
  89. package/python/biocore_model/tests/test_core_commands.py +146 -0
  90. package/python/biocore_model/tests/test_core_mode.py +161 -0
  91. package/python/biocore_model/tests/test_core_state.py +240 -0
  92. package/python/biocore_model/tests/test_criticality.py +61 -0
  93. package/python/biocore_model/tests/test_cross_session_copy.py +67 -0
  94. package/python/biocore_model/tests/test_debug_values.py +169 -0
  95. package/python/biocore_model/tests/test_defense_mechanisms.py +177 -0
  96. package/python/biocore_model/tests/test_delta_budget.py +157 -0
  97. package/python/biocore_model/tests/test_diag_trace.py +201 -0
  98. package/python/biocore_model/tests/test_directives_compat.py +698 -0
  99. package/python/biocore_model/tests/test_dissonance.py +97 -0
  100. package/python/biocore_model/tests/test_drift_sign.py +87 -0
  101. package/python/biocore_model/tests/test_embedding_fallback.py +277 -0
  102. package/python/biocore_model/tests/test_embedding_local.py +279 -0
  103. package/python/biocore_model/tests/test_embedding_mode.py +175 -0
  104. package/python/biocore_model/tests/test_embedding_modes.py +342 -0
  105. package/python/biocore_model/tests/test_emotion_geometry.py +114 -0
  106. package/python/biocore_model/tests/test_emotion_probe.py +214 -0
  107. package/python/biocore_model/tests/test_emotion_probe_concurrency.py +128 -0
  108. package/python/biocore_model/tests/test_entity_graph.py +198 -0
  109. package/python/biocore_model/tests/test_entity_resolve.py +446 -0
  110. package/python/biocore_model/tests/test_envy.py +173 -0
  111. package/python/biocore_model/tests/test_exit_or_silence.py +209 -0
  112. package/python/biocore_model/tests/test_expr_guard.py +1 -0
  113. package/python/biocore_model/tests/test_expr_intent.py +66 -0
  114. package/python/biocore_model/tests/test_expr_state.py +66 -0
  115. package/python/biocore_model/tests/test_expr_template.py +71 -0
  116. package/python/biocore_model/tests/test_expr_tone.py +41 -0
  117. package/python/biocore_model/tests/test_extreme_scenarios.py +147 -0
  118. package/python/biocore_model/tests/test_feedback_flow.py +185 -0
  119. package/python/biocore_model/tests/test_feedback_guard.py +65 -0
  120. package/python/biocore_model/tests/test_feedback_llm.py +59 -0
  121. package/python/biocore_model/tests/test_feedback_llm_inferred.py +140 -0
  122. package/python/biocore_model/tests/test_feedback_memory.py +63 -0
  123. package/python/biocore_model/tests/test_feedback_user.py +61 -0
  124. package/python/biocore_model/tests/test_frustration_curve.py +66 -0
  125. package/python/biocore_model/tests/test_global_bio_state.py +116 -0
  126. package/python/biocore_model/tests/test_governance.py +181 -0
  127. package/python/biocore_model/tests/test_granularity.py +133 -0
  128. package/python/biocore_model/tests/test_graph_extract.py +224 -0
  129. package/python/biocore_model/tests/test_graph_recall.py +226 -0
  130. package/python/biocore_model/tests/test_graph_sqlite.py +298 -0
  131. package/python/biocore_model/tests/test_growth.py +301 -0
  132. package/python/biocore_model/tests/test_intent_render.py +60 -0
  133. package/python/biocore_model/tests/test_intero_regulation.py +224 -0
  134. package/python/biocore_model/tests/test_intero_style.py +146 -0
  135. package/python/biocore_model/tests/test_item3_numeric.py +105 -0
  136. package/python/biocore_model/tests/test_item6_numeric.py +116 -0
  137. package/python/biocore_model/tests/test_layered_memory.py +174 -0
  138. package/python/biocore_model/tests/test_leak_guard.py +75 -0
  139. package/python/biocore_model/tests/test_learning_probe.py +93 -0
  140. package/python/biocore_model/tests/test_life_loop.py +222 -0
  141. package/python/biocore_model/tests/test_life_loop_10s.py +131 -0
  142. package/python/biocore_model/tests/test_local_embedding_health.py +258 -0
  143. package/python/biocore_model/tests/test_memory_affect_encoding.py +79 -0
  144. package/python/biocore_model/tests/test_memory_backend.py +332 -0
  145. package/python/biocore_model/tests/test_memory_dedup.py +316 -0
  146. package/python/biocore_model/tests/test_memory_dual.py +219 -0
  147. package/python/biocore_model/tests/test_memory_dual_wiring.py +219 -0
  148. package/python/biocore_model/tests/test_memory_feedback.py +241 -0
  149. package/python/biocore_model/tests/test_memory_fixes.py +84 -0
  150. package/python/biocore_model/tests/test_memory_forgetting.py +58 -0
  151. package/python/biocore_model/tests/test_memory_inject.py +211 -0
  152. package/python/biocore_model/tests/test_memory_mood_recall.py +118 -0
  153. package/python/biocore_model/tests/test_memory_store.py +372 -0
  154. package/python/biocore_model/tests/test_metacog.py +103 -0
  155. package/python/biocore_model/tests/test_migrate.py +245 -0
  156. package/python/biocore_model/tests/test_migrate_store.py +194 -0
  157. package/python/biocore_model/tests/test_mind_wander.py +65 -0
  158. package/python/biocore_model/tests/test_mood.py +35 -0
  159. package/python/biocore_model/tests/test_narrative.py +114 -0
  160. package/python/biocore_model/tests/test_needs_esteem.py +51 -0
  161. package/python/biocore_model/tests/test_needs_events.py +205 -0
  162. package/python/biocore_model/tests/test_needs_sleep.py +54 -0
  163. package/python/biocore_model/tests/test_noise_bank.py +54 -0
  164. package/python/biocore_model/tests/test_numeric_schema.py +98 -0
  165. package/python/biocore_model/tests/test_output_protocol.py +65 -0
  166. package/python/biocore_model/tests/test_page_debug.py +147 -0
  167. package/python/biocore_model/tests/test_page_status_autonomy.py +215 -0
  168. package/python/biocore_model/tests/test_pipeline.py +345 -0
  169. package/python/biocore_model/tests/test_pipeline_api.py +192 -0
  170. package/python/biocore_model/tests/test_pipeline_mode.py +222 -0
  171. package/python/biocore_model/tests/test_plugin_config.py +85 -0
  172. package/python/biocore_model/tests/test_policy_learning.py +116 -0
  173. package/python/biocore_model/tests/test_possible_selves.py +88 -0
  174. package/python/biocore_model/tests/test_predictor.py +227 -0
  175. package/python/biocore_model/tests/test_preference_drift.py +142 -0
  176. package/python/biocore_model/tests/test_preset.py +142 -0
  177. package/python/biocore_model/tests/test_proactive_context.py +70 -0
  178. package/python/biocore_model/tests/test_proactive_history.py +90 -0
  179. package/python/biocore_model/tests/test_proactive_memory.py +141 -0
  180. package/python/biocore_model/tests/test_proactive_send_record.py +88 -0
  181. package/python/biocore_model/tests/test_probe_contract.py +71 -0
  182. package/python/biocore_model/tests/test_prospective.py +154 -0
  183. package/python/biocore_model/tests/test_provider_fallback.py +89 -0
  184. package/python/biocore_model/tests/test_reconstruction.py +111 -0
  185. package/python/biocore_model/tests/test_regulation_strategies.py +228 -0
  186. package/python/biocore_model/tests/test_relation_stage.py +114 -0
  187. package/python/biocore_model/tests/test_relationship_stage.py +59 -0
  188. package/python/biocore_model/tests/test_release_140.py +55 -0
  189. package/python/biocore_model/tests/test_repair_after_boundary.py +249 -0
  190. package/python/biocore_model/tests/test_reply_limit.py +86 -0
  191. package/python/biocore_model/tests/test_reply_limit_persist.py +48 -0
  192. package/python/biocore_model/tests/test_reply_split.py +52 -0
  193. package/python/biocore_model/tests/test_reply_split_astrbot.py +66 -0
  194. package/python/biocore_model/tests/test_reply_split_inject.py +53 -0
  195. package/python/biocore_model/tests/test_rerank.py +229 -0
  196. package/python/biocore_model/tests/test_rollback.py +175 -0
  197. package/python/biocore_model/tests/test_runtime.py +175 -0
  198. package/python/biocore_model/tests/test_runtime_active.py +205 -0
  199. package/python/biocore_model/tests/test_runtime_pipeline.py +1583 -0
  200. package/python/biocore_model/tests/test_runtime_proactive.py +231 -0
  201. package/python/biocore_model/tests/test_scheduler.py +79 -0
  202. package/python/biocore_model/tests/test_secrecy.py +105 -0
  203. package/python/biocore_model/tests/test_self_esteem.py +45 -0
  204. package/python/biocore_model/tests/test_self_intent.py +347 -0
  205. package/python/biocore_model/tests/test_self_interest.py +183 -0
  206. package/python/biocore_model/tests/test_self_layer_wiring.py +198 -0
  207. package/python/biocore_model/tests/test_self_verification.py +113 -0
  208. package/python/biocore_model/tests/test_situation.py +142 -0
  209. package/python/biocore_model/tests/test_sleep_circadian.py +41 -0
  210. package/python/biocore_model/tests/test_social_pain.py +49 -0
  211. package/python/biocore_model/tests/test_social_repair.py +67 -0
  212. package/python/biocore_model/tests/test_social_theory.py +78 -0
  213. package/python/biocore_model/tests/test_source_monitoring.py +204 -0
  214. package/python/biocore_model/tests/test_speak_like_group.py +36 -0
  215. package/python/biocore_model/tests/test_stages.py +226 -0
  216. package/python/biocore_model/tests/test_state_tag.py +86 -0
  217. package/python/biocore_model/tests/test_template_expr.py +65 -0
  218. package/python/biocore_model/tests/test_text_inference.py +70 -0
  219. package/python/biocore_model/tests/test_theta_params.py +115 -0
  220. package/python/biocore_model/tests/test_topic_control.py +200 -0
  221. package/python/biocore_model/tests/test_turn_memory.py +105 -0
  222. package/python/biocore_model/tests/test_turn_memory_panel.py +174 -0
  223. package/python/biocore_model/tests/test_ui_settings.py +84 -0
  224. package/python/biocore_model/tests/test_ui_settings_authority.py +179 -0
  225. package/python/biocore_model/tests/test_user_appraisal_async.py +155 -0
  226. package/python/biocore_model/tests/test_user_classify.py +122 -0
  227. package/python/biocore_model/tests/test_value_order.py +117 -0
  228. package/python/biocore_model/tests/test_voice_bank.py +105 -0
  229. package/python/biocore_model/tests/test_waiting_sense.py +216 -0
  230. package/python/biocore_model/tests/test_x_directives.py +101 -0
  231. package/python/biocore_model/tests/ui_compat_check.mjs +22 -0
  232. package/python/biocore_model/tests/view_mode_check.mjs +17 -0
  233. package/python/biocore_runtime/autonomy.py +208 -0
  234. package/python/biocore_runtime/core_state.py +302 -0
  235. package/python/biocore_runtime/governance.py +495 -0
  236. package/python/biocore_runtime/growth.py +528 -0
  237. package/python/biocore_runtime/pipeline.py +850 -23
  238. package/python/biocore_runtime/relationship_state.py +214 -0
  239. package/python/biocore_runtime/self_layer.py +103 -0
  240. package/python/biocore_runtime/service.py +162 -0
  241. package/python/biocore_runtime/standby.py +472 -0
  242. package/ui/panel-layout.js +83 -39
  243. package/ui/panel.css +885 -743
  244. package/ui/panel.html +356 -296
  245. package/ui/panel.js +739 -865
@@ -0,0 +1,111 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Memory reconstruction: bounded drift, direction, conservation."""
3
+ import unittest
4
+
5
+ from biocore_model import reconstruction as rc
6
+
7
+ ENTRY = {"text": "那天一起看了海", "importance": 0.5, "valence": 0.2}
8
+
9
+
10
+ class MagnitudeTest(unittest.TestCase):
11
+ def test_zero_recalls_no_drift(self):
12
+ self.assertEqual(rc.magnitude(0), 0.0)
13
+
14
+ def test_grows_with_recalls(self):
15
+ self.assertGreater(rc.magnitude(5), rc.magnitude(1))
16
+
17
+ def test_capped(self):
18
+ self.assertLessEqual(rc.magnitude(10 ** 6), rc.DRIFT_CAP)
19
+
20
+
21
+ class DirectionTest(unittest.TestCase):
22
+ def test_warm_mood_positive(self):
23
+ self.assertEqual(rc.direction(0.6), 1)
24
+
25
+ def test_low_mood_negative(self):
26
+ self.assertEqual(rc.direction(-0.5), -1)
27
+
28
+ def test_neutral(self):
29
+ self.assertEqual(rc.direction(0.0), 0)
30
+
31
+
32
+ class DriftTest(unittest.TestCase):
33
+ def test_no_recall_no_change(self):
34
+ out = rc.drift(ENTRY, mood=0.8, recalls=0)
35
+ self.assertEqual(out["delta"], 0.0)
36
+
37
+ def test_warm_mood_moves_positive(self):
38
+ out = rc.drift(ENTRY, mood=0.8, recalls=3)
39
+ self.assertGreater(out["delta"], 0.0)
40
+
41
+ def test_low_mood_moves_negative(self):
42
+ out = rc.drift(ENTRY, mood=-0.8, recalls=3)
43
+ self.assertLess(out["delta"], 0.0)
44
+
45
+ def test_neutral_mood_no_change(self):
46
+ out = rc.drift(ENTRY, mood=0.0, recalls=3)
47
+ self.assertEqual(out["delta"], 0.0)
48
+
49
+ def test_important_memory_never_drifts(self):
50
+ row = dict(ENTRY, importance=0.95)
51
+ out = rc.drift(row, mood=0.9, recalls=20)
52
+ self.assertEqual(out["delta"], 0.0)
53
+ self.assertEqual(out["reason"], "重要记忆不漂")
54
+
55
+ def test_empty_text_no_change(self):
56
+ out = rc.drift({"importance": 0.3}, mood=0.9, recalls=3)
57
+ self.assertEqual(out["delta"], 0.0)
58
+
59
+ def test_cap_respected(self):
60
+ out = rc.drift(ENTRY, mood=1.0, recalls=1000, prior_drift=0.10)
61
+ self.assertLessEqual(abs(out["total"]), rc.DRIFT_CAP)
62
+ self.assertTrue(out["capped"])
63
+
64
+ def test_conservation_positive_side(self):
65
+ out = rc.drift(ENTRY, mood=1.0, recalls=50, prior_drift=0.10)
66
+ self.assertAlmostEqual(out["total"], rc.DRIFT_CAP, places=3)
67
+
68
+ def test_conservation_negative_side(self):
69
+ out = rc.drift(ENTRY, mood=-1.0, recalls=50, prior_drift=-0.10)
70
+ self.assertAlmostEqual(out["total"], -rc.DRIFT_CAP, places=3)
71
+
72
+ def test_disabled(self):
73
+ out = rc.drift(ENTRY, mood=0.9, recalls=5, flags={"enabled": False})
74
+ self.assertEqual(out["delta"], 0.0)
75
+ self.assertFalse(out["enabled"])
76
+
77
+ def test_malformed_entry(self):
78
+ out = rc.drift("nope", mood="x", recalls="y")
79
+ self.assertEqual(out["delta"], 0.0)
80
+
81
+ def test_bounded_without_prior(self):
82
+ out = rc.drift(ENTRY, mood=-1.0, recalls=5)
83
+ self.assertGreaterEqual(out["total"], -rc.DRIFT_CAP)
84
+
85
+
86
+ class HintTest(unittest.TestCase):
87
+ def test_no_hint_when_tiny(self):
88
+ self.assertEqual(rc.text_hint({"enabled": True, "total": 0.0}), "")
89
+
90
+ def test_warm_hint(self):
91
+ self.assertIn("暖", rc.text_hint({"enabled": True, "total": 0.05}))
92
+
93
+ def test_cold_hint(self):
94
+ self.assertIn("凉", rc.text_hint({"enabled": True, "total": -0.05}))
95
+
96
+ def test_disabled_hint(self):
97
+ self.assertEqual(rc.text_hint({"enabled": False, "total": 0.05}), "")
98
+
99
+
100
+ class ConserveTest(unittest.TestCase):
101
+ def test_within_cap_untouched(self):
102
+ out = rc.conserve([{"drift": 0.03}, {"drift": -0.02}])
103
+ self.assertEqual(out["scale"], 1.0)
104
+
105
+ def test_scales_down(self):
106
+ out = rc.conserve([{"drift": 0.3}, {"drift": 0.3}])
107
+ self.assertLessEqual(abs(out["total"]), rc.DRIFT_CAP)
108
+
109
+
110
+ if __name__ == "__main__":
111
+ unittest.main()
@@ -0,0 +1,228 @@
1
+ # -*- coding: utf-8 -*-
2
+ """task-175 调节策略库单测:Gross 五策略命中、成本/效果、关系调制、关闭。
3
+
4
+ 只依赖 biocore_model.regulation_strategies 的纯函数;不导入 main.py。
5
+ """
6
+ import unittest
7
+
8
+ from biocore_model import regulation_strategies as rs
9
+
10
+ CALM = {"closeness": 0.5, "trust": 0.5, "conflict": 0.0}
11
+
12
+ SELECT_STATE = {"stress": 0.2, "energy": 0.7}
13
+ SELECT_EVENT = {"when": "before", "controllable": 0.9, "intensity": 0.4}
14
+
15
+ MODIFY_STATE = {"stress": 0.3, "energy": 0.6}
16
+ MODIFY_EVENT = {"when": "after", "controllable": 0.8, "intensity": 0.5}
17
+ MODIFY_REL = {"closeness": 0.7, "trust": 0.6}
18
+
19
+ ATTENTION_STATE = {"stress": 0.8, "energy": 0.2, "valence": -0.3}
20
+ ATTENTION_EVENT = {"when": "after", "controllable": 0.1, "intensity": 0.9}
21
+
22
+ REAPPRAISE_STATE = {"stress": 0.2, "energy": 0.8, "valence": 0.4}
23
+ REAPPRAISE_EVENT = {"when": "after", "controllable": 0.7, "intensity": 0.4}
24
+ REAPPRAISE_REL = {"closeness": 0.9, "trust": 0.9}
25
+
26
+ MODULATE_STATE = {"stress": 0.7, "energy": 0.4}
27
+ MODULATE_EVENT = {"when": "after", "controllable": 0.5, "intensity": 0.6,
28
+ "public": True}
29
+ MODULATE_REL = {"closeness": 0.2, "trust": 0.2, "conflict": 0.8}
30
+
31
+
32
+ class ContractTest(unittest.TestCase):
33
+ def test_result_keys(self):
34
+ out = rs.evaluate(SELECT_STATE, SELECT_EVENT, CALM)
35
+ for key in ("enabled", "strategy", "label", "cost", "effect", "reason",
36
+ "self_talk", "coping_tier", "confidence", "fit", "scores",
37
+ "profiles"):
38
+ self.assertIn(key, out)
39
+
40
+ def test_five_strategies_and_labels(self):
41
+ self.assertEqual(len(rs.STRATEGIES), 5)
42
+ self.assertEqual(set(rs.LABELS), set(rs.STRATEGIES))
43
+ self.assertEqual(rs.LABELS["situation_selection"], "情境选择")
44
+ self.assertEqual(rs.LABELS["situation_modification"], "情境修正")
45
+ self.assertEqual(rs.LABELS["attentional_deployment"], "注意部署")
46
+ self.assertEqual(rs.LABELS["cognitive_reappraisal"], "认知重评")
47
+ self.assertEqual(rs.LABELS["response_modulation"], "反应调节")
48
+
49
+ def test_old_coping_tiers_coexist(self):
50
+ self.assertEqual(rs.COPING_TIERS,
51
+ ("problem_focused", "emotion_focused", "avoidance"))
52
+ self.assertEqual(set(rs.TIER_OF), set(rs.STRATEGIES))
53
+ for tier in rs.TIER_OF.values():
54
+ self.assertIn(tier, rs.COPING_TIERS)
55
+
56
+ def test_values_bounded(self):
57
+ for args in (
58
+ (SELECT_STATE, SELECT_EVENT, CALM),
59
+ (ATTENTION_STATE, ATTENTION_EVENT, CALM),
60
+ (MODULATE_STATE, MODULATE_EVENT, MODULATE_REL),
61
+ ):
62
+ out = rs.evaluate(*args)
63
+ for key in ("cost", "effect", "fit", "confidence"):
64
+ self.assertGreaterEqual(out[key], 0.0)
65
+ self.assertLessEqual(out[key], 1.0)
66
+ self.assertEqual(set(out["scores"]), set(rs.STRATEGIES))
67
+
68
+ def test_deterministic(self):
69
+ first = rs.evaluate(MODULATE_STATE, MODULATE_EVENT, MODULATE_REL)
70
+ second = rs.evaluate(MODULATE_STATE, MODULATE_EVENT, MODULATE_REL)
71
+ self.assertEqual(first, second)
72
+
73
+
74
+ class FiveStrategiesTest(unittest.TestCase):
75
+ def test_situation_selection(self):
76
+ out = rs.evaluate(SELECT_STATE, SELECT_EVENT, CALM)
77
+ self.assertEqual(out["strategy"], "situation_selection")
78
+ self.assertEqual(out["coping_tier"], "problem_focused")
79
+
80
+ def test_situation_modification(self):
81
+ out = rs.evaluate(MODIFY_STATE, MODIFY_EVENT, MODIFY_REL)
82
+ self.assertEqual(out["strategy"], "situation_modification")
83
+ self.assertEqual(out["coping_tier"], "problem_focused")
84
+
85
+ def test_attentional_deployment(self):
86
+ out = rs.evaluate(ATTENTION_STATE, ATTENTION_EVENT, CALM)
87
+ self.assertEqual(out["strategy"], "attentional_deployment")
88
+ self.assertEqual(out["coping_tier"], "avoidance")
89
+
90
+ def test_cognitive_reappraisal(self):
91
+ out = rs.evaluate(REAPPRAISE_STATE, REAPPRAISE_EVENT, REAPPRAISE_REL)
92
+ self.assertEqual(out["strategy"], "cognitive_reappraisal")
93
+ self.assertEqual(out["coping_tier"], "emotion_focused")
94
+
95
+ def test_response_modulation(self):
96
+ out = rs.evaluate(MODULATE_STATE, MODULATE_EVENT, MODULATE_REL)
97
+ self.assertEqual(out["strategy"], "response_modulation")
98
+ self.assertEqual(out["coping_tier"], "emotion_focused")
99
+ self.assertGreater(out["cost"], 0.6)
100
+
101
+
102
+ class CostEffectTest(unittest.TestCase):
103
+ def test_suppression_costs_more_than_selection(self):
104
+ selection = rs.profile("situation_selection", SELECT_STATE,
105
+ SELECT_EVENT, CALM)
106
+ modulation = rs.profile("response_modulation", MODULATE_STATE,
107
+ MODULATE_EVENT, MODULATE_REL)
108
+ self.assertGreater(modulation["cost"], selection["cost"])
109
+
110
+ def test_suppression_costs_more_than_reappraisal(self):
111
+ reappraisal = rs.profile("cognitive_reappraisal", MODULATE_STATE,
112
+ MODULATE_EVENT, MODULATE_REL)
113
+ modulation = rs.profile("response_modulation", MODULATE_STATE,
114
+ MODULATE_EVENT, MODULATE_REL)
115
+ self.assertGreater(modulation["cost"], reappraisal["cost"])
116
+
117
+ def test_reappraisal_costs_more_than_distraction(self):
118
+ state = {"energy": 0.5, "stress": 0.5}
119
+ event = {"intensity": 0.5}
120
+ reappraisal = rs.profile("cognitive_reappraisal", state, event, CALM)
121
+ attention = rs.profile("attentional_deployment", state, event, CALM)
122
+ self.assertGreater(reappraisal["cost"], attention["cost"])
123
+
124
+ def test_effect_rises_with_control(self):
125
+ low = rs.profile("situation_selection", {}, {"controllable": 0.1})
126
+ high = rs.profile("situation_selection", {}, {"controllable": 0.9})
127
+ self.assertGreater(high["effect"], low["effect"])
128
+
129
+ def test_profiles_cover_all_strategies(self):
130
+ rows = rs.profiles(SELECT_STATE, SELECT_EVENT, CALM)
131
+ self.assertEqual({row["strategy"] for row in rows},
132
+ set(rs.STRATEGIES))
133
+ for first, second in zip(rows, rows[1:]):
134
+ self.assertGreaterEqual(first["score"], second["score"])
135
+ for row in rows:
136
+ for key in ("cost", "effect", "fit", "score"):
137
+ self.assertGreaterEqual(row[key], 0.0)
138
+ self.assertLessEqual(row[key], 1.0)
139
+
140
+
141
+ class RelationshipTest(unittest.TestCase):
142
+ def test_trust_raises_reappraisal_fit(self):
143
+ near = rs.profile("cognitive_reappraisal", REAPPRAISE_STATE,
144
+ REAPPRAISE_EVENT, {"closeness": 0.9, "trust": 0.9})
145
+ far = rs.profile("cognitive_reappraisal", REAPPRAISE_STATE,
146
+ REAPPRAISE_EVENT, {"closeness": 0.1, "trust": 0.1})
147
+ self.assertGreater(near["fit"], far["fit"])
148
+
149
+ def test_close_relationship_prefers_reappraisal(self):
150
+ near = rs.evaluate(REAPPRAISE_STATE, REAPPRAISE_EVENT,
151
+ {"closeness": 0.9, "trust": 0.9})
152
+ far = rs.evaluate(REAPPRAISE_STATE, REAPPRAISE_EVENT,
153
+ {"closeness": 0.1, "trust": 0.1})
154
+ self.assertEqual(near["strategy"], "cognitive_reappraisal")
155
+ self.assertNotEqual(far["strategy"], near["strategy"])
156
+
157
+ def test_public_conflict_prefers_suppression(self):
158
+ out = rs.evaluate(MODULATE_STATE, MODULATE_EVENT, MODULATE_REL)
159
+ self.assertEqual(out["strategy"], "response_modulation")
160
+ reappraisal_fit = out["scores"]["cognitive_reappraisal"]
161
+ self.assertLess(reappraisal_fit, out["scores"]["response_modulation"])
162
+
163
+
164
+ class SwitchTest(unittest.TestCase):
165
+ def test_disabled_returns_old_coping(self):
166
+ out = rs.evaluate(SELECT_STATE, SELECT_EVENT, CALM,
167
+ flags={"enabled": False})
168
+ self.assertFalse(out["enabled"])
169
+ self.assertEqual(out["strategy"], "none")
170
+ self.assertEqual(out["cost"], 0.0)
171
+ self.assertEqual(out["effect"], 0.0)
172
+ self.assertIsNone(out["scores"].get("situation_selection"))
173
+ self.assertIn("关闭", out["reason"])
174
+
175
+ def test_directives_disabled_is_neutral(self):
176
+ out = rs.directives(rs.evaluate(flags={"enabled": False}))
177
+ self.assertEqual(out["strategy"], "none")
178
+ self.assertEqual(out["tone"], "")
179
+ self.assertEqual(out["coping_tier"], "")
180
+
181
+
182
+ class SelfTalkTest(unittest.TestCase):
183
+ def test_self_talk_exists_for_every_strategy(self):
184
+ for strategy in rs.STRATEGIES:
185
+ row = rs.profile(strategy)
186
+ self.assertTrue(row["self_talk"])
187
+ self.assertLessEqual(len(row["self_talk"]), 40)
188
+ self.assertTrue(row["reason"])
189
+
190
+ def test_directives_carry_strategy_and_tone(self):
191
+ out = rs.directives(rs.evaluate(SELECT_STATE, SELECT_EVENT, CALM))
192
+ self.assertEqual(out["strategy"], "situation_selection")
193
+ self.assertIn(out["coping_tier"], rs.COPING_TIERS)
194
+ self.assertTrue(out["tone"])
195
+ self.assertGreaterEqual(out["cost"], 0.0)
196
+ self.assertLessEqual(out["effect"], 1.0)
197
+
198
+
199
+ class RobustnessTest(unittest.TestCase):
200
+ def test_odd_inputs_still_choose(self):
201
+ for state in (None, {}, "x", 3, [], {"stress": float("nan")}):
202
+ out = rs.evaluate(state=state)
203
+ self.assertIn(out["strategy"], rs.STRATEGIES)
204
+ self.assertTrue(out["reason"])
205
+ for event in (None, "x", 3, [], {"controllable": "?"}):
206
+ self.assertIn(rs.evaluate(SELECT_STATE, event)["strategy"],
207
+ rs.STRATEGIES)
208
+ for relationship in (None, "x", 3, []):
209
+ self.assertIn(rs.evaluate(relationship=relationship)["strategy"],
210
+ rs.STRATEGIES)
211
+
212
+ def test_unknown_profile_is_neutral(self):
213
+ out = rs.profile("nope")
214
+ self.assertEqual(out["strategy"], "none")
215
+ self.assertEqual(out["cost"], 0.0)
216
+ self.assertEqual(out["score"], 0.0)
217
+
218
+ def test_confidence_within_bounds(self):
219
+ for args in ((SELECT_STATE, SELECT_EVENT, CALM),
220
+ (ATTENTION_STATE, ATTENTION_EVENT, CALM),
221
+ ({}, {}, {})):
222
+ out = rs.evaluate(*args)
223
+ self.assertGreaterEqual(out["confidence"], 0.0)
224
+ self.assertLessEqual(out["confidence"], 1.0)
225
+
226
+
227
+ if __name__ == "__main__":
228
+ unittest.main()
@@ -0,0 +1,114 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Relationship stage: progress, thresholds, actions, fairness."""
3
+ import unittest
4
+
5
+ from biocore_model import relation_stage as rs
6
+
7
+
8
+ def state(**kwargs):
9
+ base = {"interactions": 0, "disclosures": 0, "repairs": 0,
10
+ "days_known": 0, "conflict": 0.0}
11
+ base.update(kwargs)
12
+ return base
13
+
14
+
15
+ class ProgressTest(unittest.TestCase):
16
+ def test_empty_is_zero(self):
17
+ self.assertEqual(rs.progress({}), 0.0)
18
+
19
+ def test_more_interactions_more_progress(self):
20
+ low = rs.progress(state(interactions=2))
21
+ high = rs.progress(state(interactions=40))
22
+ self.assertGreater(high, low)
23
+
24
+ def test_disclosure_moves_stage_fast(self):
25
+ low = rs.progress(state(interactions=5))
26
+ high = rs.progress(state(interactions=5, disclosures=10))
27
+ self.assertGreater(high, low)
28
+
29
+ def test_repairs_add(self):
30
+ low = rs.progress(state(interactions=10, disclosures=4))
31
+ high = rs.progress(state(interactions=10, disclosures=4, repairs=3))
32
+ self.assertGreater(high, low)
33
+
34
+ def test_conflict_subtracts(self):
35
+ calm = rs.progress(state(interactions=20, disclosures=6))
36
+ tense = rs.progress(state(interactions=20, disclosures=6, conflict=0.9))
37
+ self.assertLess(tense, calm)
38
+
39
+ def test_progress_bounded(self):
40
+ value = rs.progress(state(interactions=9999, disclosures=9999, repairs=9999,
41
+ days_known=9999))
42
+ self.assertLessEqual(value, 1.0)
43
+ self.assertGreaterEqual(value, 0.0)
44
+
45
+
46
+ class StageTest(unittest.TestCase):
47
+ def test_thresholds(self):
48
+ self.assertEqual(rs.stage_of(0.1), "陌生")
49
+ self.assertEqual(rs.stage_of(0.5), "熟悉")
50
+ self.assertEqual(rs.stage_of(0.7), "亲近")
51
+ self.assertEqual(rs.stage_of(0.9), "稳定")
52
+
53
+ def test_actions_match_stages(self):
54
+ for stage in rs.STAGES:
55
+ self.assertIn(stage, rs.ACTIONS)
56
+
57
+ def test_evaluate_basic(self):
58
+ out = rs.evaluate(state(interactions=50, disclosures=12))
59
+ self.assertIn(out["stage"], rs.STAGES)
60
+ self.assertTrue(out["action"])
61
+
62
+
63
+ class FairnessTest(unittest.TestCase):
64
+ def test_balanced(self):
65
+ out = rs.comparison({"his_investment": 0.5, "hers_investment": 0.5})
66
+ self.assertEqual(out["share"], 0.5)
67
+ self.assertEqual(out["balance"], 0.0)
68
+
69
+ def test_one_sided(self):
70
+ out = rs.comparison({"his_investment": 0.9, "hers_investment": 0.1})
71
+ self.assertGreater(out["balance"], 0.5)
72
+
73
+ def test_one_sided_marks_tentative(self):
74
+ out = rs.evaluate(state(interactions=50, disclosures=12,
75
+ social_comparison={"his_investment": 0.9,
76
+ "hers_investment": 0.1}))
77
+ self.assertTrue(out["tentative"])
78
+
79
+ def test_unfairness_slows_progress(self):
80
+ fair = rs.progress(state(interactions=30, disclosures=8,
81
+ social_comparison={"his_investment": 0.5,
82
+ "hers_investment": 0.5}))
83
+ unfair = rs.progress(state(interactions=30, disclosures=8,
84
+ social_comparison={"his_investment": 0.95,
85
+ "hers_investment": 0.05}))
86
+ self.assertLess(unfair, fair)
87
+
88
+
89
+ class FallbackTest(unittest.TestCase):
90
+ def test_disabled(self):
91
+ out = rs.evaluate(state(interactions=100), flags={"enabled": False})
92
+ self.assertFalse(out["enabled"])
93
+ self.assertEqual(out["progress"], 0.0)
94
+
95
+ def test_malformed(self):
96
+ out = rs.evaluate("nope")
97
+ self.assertIn(out["stage"], rs.STAGES)
98
+
99
+
100
+ class DirectiveTest(unittest.TestCase):
101
+ def test_stage_tone(self):
102
+ out = rs.directives({"enabled": True, "stage": "亲近"})
103
+ self.assertIn("主动", out["tone"])
104
+
105
+ def test_tentative_note(self):
106
+ out = rs.directives({"enabled": True, "stage": "熟悉", "tentative": True})
107
+ self.assertIn("不对等", out["tone"])
108
+
109
+ def test_disabled(self):
110
+ self.assertEqual(rs.directives({"enabled": False})["tone"], "")
111
+
112
+
113
+ if __name__ == "__main__":
114
+ unittest.main()
@@ -0,0 +1,59 @@
1
+ # -*- coding: utf-8 -*-
2
+ import random, unittest
3
+ from biocore_model import relationship_stage as rs
4
+
5
+ NOW = 1_700_000_000
6
+
7
+ class StageTest(unittest.TestCase):
8
+ def test_stranger(self):
9
+ self.assertEqual(rs.stage_for(5), 'stranger')
10
+ def test_acquaintance(self):
11
+ self.assertEqual(rs.stage_for(10), 'acquaintance')
12
+ def test_friend(self):
13
+ self.assertEqual(rs.stage_for(55), 'friend')
14
+ def test_close(self):
15
+ self.assertEqual(rs.stage_for(91), 'close')
16
+ def test_boundary_50(self):
17
+ self.assertEqual(rs.stage_for(50), 'friend')
18
+ def test_boundary_90(self):
19
+ self.assertEqual(rs.stage_for(90), 'close')
20
+ def test_friend_config(self):
21
+ cfg = rs.stage_config(55)
22
+ self.assertEqual(cfg['tau_hours'], 24); self.assertEqual(cfg['daily_cap'], 5)
23
+ def test_stranger_config(self):
24
+ cfg = rs.stage_config(0)
25
+ self.assertEqual(cfg['tau_hours'], 8); self.assertEqual(cfg['daily_cap'], 1)
26
+ def test_min_interval(self):
27
+ cfg = rs.stage_config(55)
28
+ self.assertAlmostEqual(cfg['min_interval_hours'], round(24 / (5 * 0.6), 2))
29
+
30
+ class HabituationTest(unittest.TestCase):
31
+ def test_novelty_decay(self):
32
+ self.assertAlmostEqual(rs.novelty(0), 1.0)
33
+ self.assertAlmostEqual(rs.novelty(2), round(0.85 ** 2, 4))
34
+ def test_disabled_below_threshold(self):
35
+ usage = {'t1': {'count': 8, 'last_ts': NOW - 60}}
36
+ info = rs.template_weight('t1', usage, NOW)
37
+ self.assertTrue(info['disabled']); self.assertLess(info['weight'], 0.3)
38
+ def test_not_disabled_after_24h(self):
39
+ usage = {'t1': {'count': 8, 'last_ts': NOW - 25 * 3600}}
40
+ self.assertFalse(rs.template_weight('t1', usage, NOW)['disabled'])
41
+ def test_window_reset(self):
42
+ usage = {'t1': {'count': 8, 'last_ts': NOW - 8 * 86400}}
43
+ info = rs.template_weight('t1', usage, NOW)
44
+ self.assertEqual(info['count'], 0)
45
+ def test_record_template(self):
46
+ usage = rs.record_template({}, 't1', NOW)
47
+ self.assertEqual(usage['t1']['count'], 1)
48
+ def test_templates_view(self):
49
+ usage = {'t1': {'count': 9, 'last_ts': NOW - 10}, 't2': {'count': 1, 'last_ts': NOW - 10}}
50
+ view = rs.templates_view(usage, NOW)
51
+ self.assertIn('t1', view['disabled']); self.assertEqual(view['top_used'][0]['template_id'], 't1')
52
+ def test_pick_weighted_skips_disabled(self):
53
+ usage = {'t1': {'count': 9, 'last_ts': NOW - 10}}
54
+ self.assertEqual(rs.pick_weighted(['t1'], usage, NOW, random.Random(0)), None)
55
+ def test_pick_weighted_returns_candidate(self):
56
+ self.assertEqual(rs.pick_weighted(['t1', 't2'], {}, NOW, random.Random(0)) in ('t1', 't2'), True)
57
+
58
+ if __name__ == '__main__':
59
+ unittest.main()
@@ -0,0 +1,55 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Release consistency + autonomy defaults (version taken from metadata)."""
3
+ import json
4
+ import unittest
5
+ from pathlib import Path
6
+
7
+ ROOT = Path(__file__).resolve().parents[2]
8
+ PLUG = ROOT
9
+
10
+
11
+ class VersionTest(unittest.TestCase):
12
+ def test_all_version_sources_match_metadata(self):
13
+ meta = (PLUG / "metadata.yaml").read_text(encoding="utf-8")
14
+ version = None
15
+ for line in meta.splitlines():
16
+ if line.startswith("version:"):
17
+ version = line.split(":", 1)[1].strip()
18
+ break
19
+ self.assertIsNotNone(version)
20
+ main = (PLUG / "main.py").read_text(encoding="utf-8")
21
+ self.assertIn('"%s")' % version, main)
22
+ app = (PLUG / "pages" / "dashboard" / "app.js").read_text(encoding="utf-8")
23
+ self.assertIn('BIO_VERSION = "v%s"' % version, app)
24
+ index = (PLUG / "pages" / "dashboard" / "index.html").read_text(encoding="utf-8")
25
+ self.assertIn('BIO_VERSION = "v%s"' % version, index)
26
+ readme = (PLUG / "README.md").read_text(encoding="utf-8")
27
+ self.assertIn(version, readme)
28
+ record = json.loads((ROOT.parent / "docs" / "BIOCORE-MARKET-RECORD-v1.json")
29
+ .read_text(encoding="utf-8"))
30
+ rec = record["Johnhmzm/astrbot_plugin_biocore"]
31
+ self.assertEqual(rec["version"], version)
32
+
33
+
34
+ class AutonomyDefaultTest(unittest.TestCase):
35
+ def test_schema_defaults_false(self):
36
+ schema = json.loads((PLUG / "_conf_schema.json").read_text(encoding="utf-8"))
37
+ self.assertIs(schema["proactive_autonomy"]["default"], False)
38
+ self.assertIs(schema["proactive_send"]["default"], False)
39
+ self.assertIs(schema["autonomy_loop_enabled"]["default"], False)
40
+
41
+ def test_code_reads_config_with_false_default(self):
42
+ main = (PLUG / "main.py").read_text(encoding="utf-8")
43
+ self.assertIn('self.proactive_autonomy = bool(self.config.get("proactive_autonomy", False))', main)
44
+ self.assertIn('self.proactive_send = bool(self.config.get("proactive_send", False))', main)
45
+
46
+ def test_changelog_warnings_and_rollback(self):
47
+ text = (PLUG / "CHANGELOG.md").read_text(encoding="utf-8")
48
+ self.assertIn("本版本改动较大", text)
49
+ self.assertIn("自主主动回复默认关闭", text)
50
+ self.assertIn("升级失败可回滚", text)
51
+ self.assertIn("docs/MIGRATION.md", text)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ unittest.main()