lex-agentic-social 0.1.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 (235) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +13 -0
  6. data/lex-agentic-social.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/social/apprenticeship/client.rb +28 -0
  8. data/lib/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship.rb +90 -0
  9. data/lib/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_engine.rb +109 -0
  10. data/lib/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_model.rb +93 -0
  11. data/lib/legion/extensions/agentic/social/apprenticeship/runners/cognitive_apprenticeship.rb +112 -0
  12. data/lib/legion/extensions/agentic/social/apprenticeship/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/social/apprenticeship.rb +19 -0
  14. data/lib/legion/extensions/agentic/social/conflict/actors/stale_check.rb +45 -0
  15. data/lib/legion/extensions/agentic/social/conflict/client.rb +27 -0
  16. data/lib/legion/extensions/agentic/social/conflict/helpers/conflict_log.rb +70 -0
  17. data/lib/legion/extensions/agentic/social/conflict/helpers/llm_enhancer.rb +130 -0
  18. data/lib/legion/extensions/agentic/social/conflict/helpers/severity.rb +47 -0
  19. data/lib/legion/extensions/agentic/social/conflict/runners/conflict.rb +112 -0
  20. data/lib/legion/extensions/agentic/social/conflict/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/social/conflict.rb +19 -0
  22. data/lib/legion/extensions/agentic/social/conscience/client.rb +26 -0
  23. data/lib/legion/extensions/agentic/social/conscience/helpers/constants.rb +53 -0
  24. data/lib/legion/extensions/agentic/social/conscience/helpers/moral_evaluator.rb +178 -0
  25. data/lib/legion/extensions/agentic/social/conscience/helpers/moral_store.rb +116 -0
  26. data/lib/legion/extensions/agentic/social/conscience/runners/conscience.rb +117 -0
  27. data/lib/legion/extensions/agentic/social/conscience/version.rb +13 -0
  28. data/lib/legion/extensions/agentic/social/conscience.rb +19 -0
  29. data/lib/legion/extensions/agentic/social/consent/actors/tier_evaluation.rb +45 -0
  30. data/lib/legion/extensions/agentic/social/consent/client.rb +27 -0
  31. data/lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb +199 -0
  32. data/lib/legion/extensions/agentic/social/consent/helpers/tiers.rb +54 -0
  33. data/lib/legion/extensions/agentic/social/consent/local_migrations/20260316000010_create_consent_domains.rb +16 -0
  34. data/lib/legion/extensions/agentic/social/consent/runners/consent.rb +228 -0
  35. data/lib/legion/extensions/agentic/social/consent/version.rb +13 -0
  36. data/lib/legion/extensions/agentic/social/consent.rb +25 -0
  37. data/lib/legion/extensions/agentic/social/entrainment/client.rb +19 -0
  38. data/lib/legion/extensions/agentic/social/entrainment/helpers/constants.rb +40 -0
  39. data/lib/legion/extensions/agentic/social/entrainment/helpers/entrainment_engine.rb +120 -0
  40. data/lib/legion/extensions/agentic/social/entrainment/helpers/pairing.rb +86 -0
  41. data/lib/legion/extensions/agentic/social/entrainment/runners/cognitive_entrainment.rb +89 -0
  42. data/lib/legion/extensions/agentic/social/entrainment/version.rb +13 -0
  43. data/lib/legion/extensions/agentic/social/entrainment.rb +19 -0
  44. data/lib/legion/extensions/agentic/social/governance/actors/shadow_ai_scan.rb +19 -0
  45. data/lib/legion/extensions/agentic/social/governance/actors/vote_timeout.rb +45 -0
  46. data/lib/legion/extensions/agentic/social/governance/client.rb +27 -0
  47. data/lib/legion/extensions/agentic/social/governance/helpers/layers.rb +40 -0
  48. data/lib/legion/extensions/agentic/social/governance/helpers/proposal.rb +94 -0
  49. data/lib/legion/extensions/agentic/social/governance/runners/governance.rb +87 -0
  50. data/lib/legion/extensions/agentic/social/governance/runners/shadow_ai.rb +93 -0
  51. data/lib/legion/extensions/agentic/social/governance/version.rb +13 -0
  52. data/lib/legion/extensions/agentic/social/governance.rb +20 -0
  53. data/lib/legion/extensions/agentic/social/joint_attention/actors/decay.rb +45 -0
  54. data/lib/legion/extensions/agentic/social/joint_attention/client.rb +28 -0
  55. data/lib/legion/extensions/agentic/social/joint_attention/helpers/attention_target.rb +124 -0
  56. data/lib/legion/extensions/agentic/social/joint_attention/helpers/constants.rb +34 -0
  57. data/lib/legion/extensions/agentic/social/joint_attention/helpers/joint_focus_manager.rb +157 -0
  58. data/lib/legion/extensions/agentic/social/joint_attention/runners/joint_attention.rb +88 -0
  59. data/lib/legion/extensions/agentic/social/joint_attention/version.rb +13 -0
  60. data/lib/legion/extensions/agentic/social/joint_attention.rb +20 -0
  61. data/lib/legion/extensions/agentic/social/mentalizing/actors/decay.rb +45 -0
  62. data/lib/legion/extensions/agentic/social/mentalizing/client.rb +28 -0
  63. data/lib/legion/extensions/agentic/social/mentalizing/helpers/belief_attribution.rb +58 -0
  64. data/lib/legion/extensions/agentic/social/mentalizing/helpers/constants.rb +33 -0
  65. data/lib/legion/extensions/agentic/social/mentalizing/helpers/mental_model.rb +137 -0
  66. data/lib/legion/extensions/agentic/social/mentalizing/runners/mentalizing.rb +93 -0
  67. data/lib/legion/extensions/agentic/social/mentalizing/version.rb +13 -0
  68. data/lib/legion/extensions/agentic/social/mentalizing.rb +20 -0
  69. data/lib/legion/extensions/agentic/social/mirror/client.rb +33 -0
  70. data/lib/legion/extensions/agentic/social/mirror/helpers/constants.rb +65 -0
  71. data/lib/legion/extensions/agentic/social/mirror/helpers/mirror_engine.rb +132 -0
  72. data/lib/legion/extensions/agentic/social/mirror/helpers/mirror_event.rb +46 -0
  73. data/lib/legion/extensions/agentic/social/mirror/helpers/simulation.rb +43 -0
  74. data/lib/legion/extensions/agentic/social/mirror/runners/observe.rb +52 -0
  75. data/lib/legion/extensions/agentic/social/mirror/runners/resonance.rb +79 -0
  76. data/lib/legion/extensions/agentic/social/mirror/runners/simulate.rb +63 -0
  77. data/lib/legion/extensions/agentic/social/mirror/version.rb +13 -0
  78. data/lib/legion/extensions/agentic/social/mirror.rb +22 -0
  79. data/lib/legion/extensions/agentic/social/mirror_system/actors/decay.rb +45 -0
  80. data/lib/legion/extensions/agentic/social/mirror_system/client.rb +28 -0
  81. data/lib/legion/extensions/agentic/social/mirror_system/helpers/constants.rb +62 -0
  82. data/lib/legion/extensions/agentic/social/mirror_system/helpers/mirror_system.rb +162 -0
  83. data/lib/legion/extensions/agentic/social/mirror_system/helpers/observed_behavior.rb +67 -0
  84. data/lib/legion/extensions/agentic/social/mirror_system/runners/mirror.rb +99 -0
  85. data/lib/legion/extensions/agentic/social/mirror_system/version.rb +13 -0
  86. data/lib/legion/extensions/agentic/social/mirror_system.rb +20 -0
  87. data/lib/legion/extensions/agentic/social/moral_reasoning/client.rb +19 -0
  88. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/constants.rb +49 -0
  89. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/dilemma.rb +68 -0
  90. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/llm_enhancer.rb +140 -0
  91. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/moral_engine.rb +239 -0
  92. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/moral_foundation.rb +45 -0
  93. data/lib/legion/extensions/agentic/social/moral_reasoning/runners/moral_reasoning.rb +121 -0
  94. data/lib/legion/extensions/agentic/social/moral_reasoning/version.rb +13 -0
  95. data/lib/legion/extensions/agentic/social/moral_reasoning.rb +21 -0
  96. data/lib/legion/extensions/agentic/social/perspective_shifting/client.rb +29 -0
  97. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/constants.rb +67 -0
  98. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/perspective.rb +45 -0
  99. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/perspective_view.rb +57 -0
  100. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb +166 -0
  101. data/lib/legion/extensions/agentic/social/perspective_shifting/runners/perspective_shifting.rb +167 -0
  102. data/lib/legion/extensions/agentic/social/perspective_shifting/version.rb +13 -0
  103. data/lib/legion/extensions/agentic/social/perspective_shifting.rb +20 -0
  104. data/lib/legion/extensions/agentic/social/social/client.rb +25 -0
  105. data/lib/legion/extensions/agentic/social/social/helpers/constants.rb +84 -0
  106. data/lib/legion/extensions/agentic/social/social/helpers/social_graph.rb +172 -0
  107. data/lib/legion/extensions/agentic/social/social/runners/social.rb +146 -0
  108. data/lib/legion/extensions/agentic/social/social/version.rb +13 -0
  109. data/lib/legion/extensions/agentic/social/social.rb +18 -0
  110. data/lib/legion/extensions/agentic/social/social_learning/client.rb +25 -0
  111. data/lib/legion/extensions/agentic/social/social_learning/helpers/constants.rb +42 -0
  112. data/lib/legion/extensions/agentic/social/social_learning/helpers/model_agent.rb +82 -0
  113. data/lib/legion/extensions/agentic/social/social_learning/helpers/observed_behavior.rb +61 -0
  114. data/lib/legion/extensions/agentic/social/social_learning/helpers/social_learning_engine.rb +134 -0
  115. data/lib/legion/extensions/agentic/social/social_learning/runners/social_learning.rb +105 -0
  116. data/lib/legion/extensions/agentic/social/social_learning/version.rb +13 -0
  117. data/lib/legion/extensions/agentic/social/social_learning.rb +20 -0
  118. data/lib/legion/extensions/agentic/social/symbiosis/client.rb +23 -0
  119. data/lib/legion/extensions/agentic/social/symbiosis/helpers/constants.rb +50 -0
  120. data/lib/legion/extensions/agentic/social/symbiosis/helpers/ecosystem.rb +113 -0
  121. data/lib/legion/extensions/agentic/social/symbiosis/helpers/symbiosis_engine.rb +104 -0
  122. data/lib/legion/extensions/agentic/social/symbiosis/helpers/symbiotic_bond.rb +112 -0
  123. data/lib/legion/extensions/agentic/social/symbiosis/runners/cognitive_symbiosis.rb +101 -0
  124. data/lib/legion/extensions/agentic/social/symbiosis/version.rb +13 -0
  125. data/lib/legion/extensions/agentic/social/symbiosis.rb +22 -0
  126. data/lib/legion/extensions/agentic/social/theory_of_mind/client.rb +26 -0
  127. data/lib/legion/extensions/agentic/social/theory_of_mind/helpers/agent_model.rb +173 -0
  128. data/lib/legion/extensions/agentic/social/theory_of_mind/helpers/constants.rb +70 -0
  129. data/lib/legion/extensions/agentic/social/theory_of_mind/helpers/mental_state_tracker.rb +169 -0
  130. data/lib/legion/extensions/agentic/social/theory_of_mind/runners/theory_of_mind.rb +159 -0
  131. data/lib/legion/extensions/agentic/social/theory_of_mind/version.rb +13 -0
  132. data/lib/legion/extensions/agentic/social/theory_of_mind.rb +19 -0
  133. data/lib/legion/extensions/agentic/social/trust/actors/decay.rb +45 -0
  134. data/lib/legion/extensions/agentic/social/trust/client.rb +27 -0
  135. data/lib/legion/extensions/agentic/social/trust/helpers/trust_map.rb +160 -0
  136. data/lib/legion/extensions/agentic/social/trust/helpers/trust_model.rb +52 -0
  137. data/lib/legion/extensions/agentic/social/trust/local_migrations/20260316000020_create_trust_entries.rb +23 -0
  138. data/lib/legion/extensions/agentic/social/trust/runners/trust.rb +80 -0
  139. data/lib/legion/extensions/agentic/social/trust/version.rb +13 -0
  140. data/lib/legion/extensions/agentic/social/trust.rb +25 -0
  141. data/lib/legion/extensions/agentic/social/version.rb +11 -0
  142. data/lib/legion/extensions/agentic/social.rb +34 -0
  143. data/spec/legion/extensions/agentic/social/apprenticeship/client_spec.rb +20 -0
  144. data/spec/legion/extensions/agentic/social/apprenticeship/cognitive_apprenticeship_spec.rb +11 -0
  145. data/spec/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_engine_spec.rb +146 -0
  146. data/spec/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_model_spec.rb +124 -0
  147. data/spec/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_spec.rb +136 -0
  148. data/spec/legion/extensions/agentic/social/apprenticeship/runners/cognitive_apprenticeship_spec.rb +154 -0
  149. data/spec/legion/extensions/agentic/social/conflict/actors/stale_check_spec.rb +45 -0
  150. data/spec/legion/extensions/agentic/social/conflict/client_spec.rb +15 -0
  151. data/spec/legion/extensions/agentic/social/conflict/helpers/conflict_log_spec.rb +232 -0
  152. data/spec/legion/extensions/agentic/social/conflict/helpers/llm_enhancer_spec.rb +189 -0
  153. data/spec/legion/extensions/agentic/social/conflict/helpers/severity_spec.rb +215 -0
  154. data/spec/legion/extensions/agentic/social/conflict/runners/conflict_spec.rb +151 -0
  155. data/spec/legion/extensions/agentic/social/conscience/client_spec.rb +58 -0
  156. data/spec/legion/extensions/agentic/social/conscience/helpers/constants_spec.rb +124 -0
  157. data/spec/legion/extensions/agentic/social/conscience/helpers/moral_evaluator_spec.rb +253 -0
  158. data/spec/legion/extensions/agentic/social/conscience/helpers/moral_store_spec.rb +230 -0
  159. data/spec/legion/extensions/agentic/social/conscience/runners/conscience_spec.rb +239 -0
  160. data/spec/legion/extensions/agentic/social/consent/actors/tier_evaluation_spec.rb +46 -0
  161. data/spec/legion/extensions/agentic/social/consent/client_spec.rb +33 -0
  162. data/spec/legion/extensions/agentic/social/consent/helpers/tiers_spec.rb +49 -0
  163. data/spec/legion/extensions/agentic/social/consent/local_persistence_spec.rb +234 -0
  164. data/spec/legion/extensions/agentic/social/consent/runners/consent_spec.rb +224 -0
  165. data/spec/legion/extensions/agentic/social/entrainment/client_spec.rb +21 -0
  166. data/spec/legion/extensions/agentic/social/entrainment/helpers/entrainment_engine_spec.rb +116 -0
  167. data/spec/legion/extensions/agentic/social/entrainment/helpers/pairing_spec.rb +103 -0
  168. data/spec/legion/extensions/agentic/social/entrainment/runners/cognitive_entrainment_spec.rb +87 -0
  169. data/spec/legion/extensions/agentic/social/governance/actors/vote_timeout_spec.rb +45 -0
  170. data/spec/legion/extensions/agentic/social/governance/client_spec.rb +14 -0
  171. data/spec/legion/extensions/agentic/social/governance/helpers/layers_spec.rb +190 -0
  172. data/spec/legion/extensions/agentic/social/governance/helpers/proposal_spec.rb +188 -0
  173. data/spec/legion/extensions/agentic/social/governance/runners/governance_spec.rb +101 -0
  174. data/spec/legion/extensions/agentic/social/governance/runners/shadow_ai_spec.rb +65 -0
  175. data/spec/legion/extensions/agentic/social/joint_attention/client_spec.rb +36 -0
  176. data/spec/legion/extensions/agentic/social/joint_attention/helpers/attention_target_spec.rb +258 -0
  177. data/spec/legion/extensions/agentic/social/joint_attention/helpers/joint_focus_manager_spec.rb +238 -0
  178. data/spec/legion/extensions/agentic/social/joint_attention/runners/joint_attention_spec.rb +228 -0
  179. data/spec/legion/extensions/agentic/social/mentalizing/client_spec.rb +19 -0
  180. data/spec/legion/extensions/agentic/social/mentalizing/helpers/belief_attribution_spec.rb +108 -0
  181. data/spec/legion/extensions/agentic/social/mentalizing/helpers/mental_model_spec.rb +179 -0
  182. data/spec/legion/extensions/agentic/social/mentalizing/runners/mentalizing_spec.rb +162 -0
  183. data/spec/legion/extensions/agentic/social/mirror/client_spec.rb +92 -0
  184. data/spec/legion/extensions/agentic/social/mirror/helpers/constants_spec.rb +123 -0
  185. data/spec/legion/extensions/agentic/social/mirror/helpers/mirror_engine_spec.rb +217 -0
  186. data/spec/legion/extensions/agentic/social/mirror/helpers/mirror_event_spec.rb +102 -0
  187. data/spec/legion/extensions/agentic/social/mirror/helpers/simulation_spec.rb +100 -0
  188. data/spec/legion/extensions/agentic/social/mirror/runners/observe_spec.rb +77 -0
  189. data/spec/legion/extensions/agentic/social/mirror/runners/resonance_spec.rb +123 -0
  190. data/spec/legion/extensions/agentic/social/mirror/runners/simulate_spec.rb +103 -0
  191. data/spec/legion/extensions/agentic/social/mirror_system/client_spec.rb +40 -0
  192. data/spec/legion/extensions/agentic/social/mirror_system/helpers/mirror_system_spec.rb +144 -0
  193. data/spec/legion/extensions/agentic/social/mirror_system/helpers/observed_behavior_spec.rb +98 -0
  194. data/spec/legion/extensions/agentic/social/mirror_system/runners/mirror_spec.rb +122 -0
  195. data/spec/legion/extensions/agentic/social/moral_reasoning/client_spec.rb +34 -0
  196. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/dilemma_spec.rb +108 -0
  197. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/llm_enhancer_spec.rb +232 -0
  198. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/moral_engine_spec.rb +266 -0
  199. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/moral_foundation_spec.rb +70 -0
  200. data/spec/legion/extensions/agentic/social/moral_reasoning/runners/moral_reasoning_spec.rb +275 -0
  201. data/spec/legion/extensions/agentic/social/perspective_shifting/client_spec.rb +30 -0
  202. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/constants_spec.rb +99 -0
  203. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/perspective_spec.rb +77 -0
  204. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/perspective_view_spec.rb +105 -0
  205. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine_spec.rb +246 -0
  206. data/spec/legion/extensions/agentic/social/perspective_shifting/runners/perspective_shifting_spec.rb +277 -0
  207. data/spec/legion/extensions/agentic/social/social/client_spec.rb +72 -0
  208. data/spec/legion/extensions/agentic/social/social/helpers/constants_spec.rb +99 -0
  209. data/spec/legion/extensions/agentic/social/social/helpers/social_graph_spec.rb +322 -0
  210. data/spec/legion/extensions/agentic/social/social/runners/social_spec.rb +220 -0
  211. data/spec/legion/extensions/agentic/social/social_learning/client_spec.rb +25 -0
  212. data/spec/legion/extensions/agentic/social/social_learning/helpers/constants_spec.rb +44 -0
  213. data/spec/legion/extensions/agentic/social/social_learning/helpers/model_agent_spec.rb +120 -0
  214. data/spec/legion/extensions/agentic/social/social_learning/helpers/observed_behavior_spec.rb +81 -0
  215. data/spec/legion/extensions/agentic/social/social_learning/helpers/social_learning_engine_spec.rb +196 -0
  216. data/spec/legion/extensions/agentic/social/social_learning/runners/social_learning_spec.rb +150 -0
  217. data/spec/legion/extensions/agentic/social/symbiosis/client_spec.rb +45 -0
  218. data/spec/legion/extensions/agentic/social/symbiosis/helpers/constants_spec.rb +73 -0
  219. data/spec/legion/extensions/agentic/social/symbiosis/helpers/ecosystem_spec.rb +185 -0
  220. data/spec/legion/extensions/agentic/social/symbiosis/helpers/symbiosis_engine_spec.rb +182 -0
  221. data/spec/legion/extensions/agentic/social/symbiosis/helpers/symbiotic_bond_spec.rb +209 -0
  222. data/spec/legion/extensions/agentic/social/symbiosis/runners/cognitive_symbiosis_spec.rb +182 -0
  223. data/spec/legion/extensions/agentic/social/theory_of_mind/client_spec.rb +63 -0
  224. data/spec/legion/extensions/agentic/social/theory_of_mind/helpers/agent_model_spec.rb +244 -0
  225. data/spec/legion/extensions/agentic/social/theory_of_mind/helpers/constants_spec.rb +71 -0
  226. data/spec/legion/extensions/agentic/social/theory_of_mind/helpers/mental_state_tracker_spec.rb +228 -0
  227. data/spec/legion/extensions/agentic/social/theory_of_mind/runners/theory_of_mind_spec.rb +221 -0
  228. data/spec/legion/extensions/agentic/social/trust/actors/decay_spec.rb +62 -0
  229. data/spec/legion/extensions/agentic/social/trust/client_spec.rb +17 -0
  230. data/spec/legion/extensions/agentic/social/trust/helpers/trust_map_spec.rb +299 -0
  231. data/spec/legion/extensions/agentic/social/trust/helpers/trust_model_spec.rb +179 -0
  232. data/spec/legion/extensions/agentic/social/trust/local_persistence_spec.rb +359 -0
  233. data/spec/legion/extensions/agentic/social/trust/runners/trust_spec.rb +84 -0
  234. data/spec/spec_helper.rb +54 -0
  235. metadata +319 -0
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module MoralReasoning
8
+ module Helpers
9
+ module LlmEnhancer
10
+ SYSTEM_PROMPT = <<~PROMPT
11
+ You are the moral reasoning engine for an autonomous AI agent built on LegionIO.
12
+ You apply ethical frameworks to evaluate actions and resolve dilemmas.
13
+ Be rigorous, analytical, and fair. Consider multiple perspectives.
14
+ Output structured reasoning, not opinions. Be concise.
15
+ PROMPT
16
+
17
+ module_function
18
+
19
+ def available?
20
+ !!(defined?(Legion::LLM) && Legion::LLM.respond_to?(:started?) && Legion::LLM.started?)
21
+ rescue StandardError
22
+ false
23
+ end
24
+
25
+ def evaluate_action(action:, description:, foundations:)
26
+ response = llm_ask(build_evaluate_action_prompt(action: action, description: description,
27
+ foundations: foundations))
28
+ parse_evaluate_action_response(response)
29
+ rescue StandardError => e
30
+ Legion::Logging.warn "[moral_reasoning:llm] evaluate_action failed: #{e.message}"
31
+ nil
32
+ end
33
+
34
+ def resolve_dilemma(dilemma_description:, options:, framework:)
35
+ response = llm_ask(build_resolve_dilemma_prompt(dilemma_description: dilemma_description,
36
+ options: options, framework: framework))
37
+ parse_resolve_dilemma_response(response)
38
+ rescue StandardError => e
39
+ Legion::Logging.warn "[moral_reasoning:llm] resolve_dilemma failed: #{e.message}"
40
+ nil
41
+ end
42
+
43
+ def llm_ask(prompt)
44
+ chat = Legion::LLM.chat
45
+ chat.with_instructions(SYSTEM_PROMPT)
46
+ chat.ask(prompt)
47
+ end
48
+ private_class_method :llm_ask
49
+
50
+ def build_evaluate_action_prompt(action:, description:, foundations:)
51
+ foundation_lines = foundations.map { |name, strength| " #{name}: #{strength.round(3)}" }.join("\n")
52
+ desc = description.to_s.empty? ? 'no description' : description
53
+ <<~PROMPT
54
+ Evaluate this action using moral foundations theory.
55
+
56
+ ACTION: #{action}
57
+ DESCRIPTION: #{desc}
58
+
59
+ Current foundation strengths:
60
+ #{foundation_lines}
61
+
62
+ For each foundation, estimate the moral impact of this action (-1.0 to 1.0).
63
+ Negative = harmful to that foundation, Positive = reinforces that foundation.
64
+
65
+ Format EXACTLY as:
66
+ REASONING: <1-2 paragraph analysis>
67
+ IMPACT: care=<float> | fairness=<float> | loyalty=<float> | authority=<float> | sanctity=<float> | liberty=<float>
68
+ PROMPT
69
+ end
70
+ private_class_method :build_evaluate_action_prompt
71
+
72
+ def parse_evaluate_action_response(response)
73
+ return nil unless response&.content
74
+
75
+ text = response.content
76
+ reasoning_match = text.match(/REASONING:\s*(.+?)(?=\nIMPACT:|\z)/im)
77
+ impact_match = text.match(/IMPACT:\s*(.+)/i)
78
+ return nil unless reasoning_match && impact_match
79
+
80
+ foundation_impacts = parse_impact_string(impact_match.captures.first.strip)
81
+ return nil if foundation_impacts.empty?
82
+
83
+ { reasoning: reasoning_match.captures.first.strip, foundation_impacts: foundation_impacts }
84
+ end
85
+ private_class_method :parse_evaluate_action_response
86
+
87
+ def parse_impact_string(impact_str)
88
+ impact_str.split('|').each_with_object({}) do |pair, hash|
89
+ key, val = pair.strip.split('=')
90
+ hash[key.strip.to_sym] = val.strip.to_f.clamp(-1.0, 1.0) if key && val
91
+ end
92
+ end
93
+ private_class_method :parse_impact_string
94
+
95
+ def build_resolve_dilemma_prompt(dilemma_description:, options:, framework:)
96
+ option_lines = options.map do |opt|
97
+ foundations_str = opt.fetch(:foundations, []).join(', ')
98
+ "- #{opt[:id] || opt[:action]}: #{opt[:description]} (foundations: #{foundations_str})"
99
+ end.join("\n")
100
+ <<~PROMPT
101
+ Resolve this moral dilemma using the #{framework} ethical framework.
102
+
103
+ DILEMMA: #{dilemma_description}
104
+
105
+ OPTIONS:
106
+ #{option_lines}
107
+
108
+ Apply #{framework} reasoning to select the best option.
109
+
110
+ Format EXACTLY as:
111
+ CHOSEN: <option label>
112
+ CONFIDENCE: <0.0-1.0>
113
+ REASONING: <1-2 paragraph justification using the specified framework>
114
+ PROMPT
115
+ end
116
+ private_class_method :build_resolve_dilemma_prompt
117
+
118
+ def parse_resolve_dilemma_response(response)
119
+ return nil unless response&.content
120
+
121
+ text = response.content
122
+ chosen_match = text.match(/CHOSEN:\s*(.+)/i)
123
+ confidence_match = text.match(/CONFIDENCE:\s*([\d.]+)/i)
124
+ reasoning_match = text.match(/REASONING:\s*(.+)/im)
125
+ return nil unless chosen_match && confidence_match && reasoning_match
126
+
127
+ {
128
+ chosen_option: chosen_match.captures.first.strip,
129
+ confidence: confidence_match.captures.first.strip.to_f.clamp(0.0, 1.0),
130
+ reasoning: reasoning_match.captures.first.strip
131
+ }
132
+ end
133
+ private_class_method :parse_resolve_dilemma_response
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module MoralReasoning
8
+ module Helpers
9
+ module FrameworkEvaluators
10
+ CARE_FOUNDATIONS = %i[care fairness sanctity].freeze
11
+ DUTY_FOUNDATIONS = %i[authority loyalty].freeze
12
+ JUSTICE_FOUNDATIONS = %i[fairness liberty].freeze
13
+ RIGHTS_FOUNDATIONS = %i[liberty fairness authority].freeze
14
+
15
+ private
16
+
17
+ def evaluate_by_utility(dilemma)
18
+ rank_options(dilemma) do |option|
19
+ option.fetch(:foundations, []).sum { |fid| @foundations[fid]&.weight.to_f }
20
+ end
21
+ end
22
+
23
+ def evaluate_by_duty(dilemma)
24
+ rank_options(dilemma) do |option|
25
+ option.fetch(:foundations, []).count { |fid| DUTY_FOUNDATIONS.include?(fid) }.to_f
26
+ end
27
+ end
28
+
29
+ def evaluate_by_virtue(dilemma)
30
+ rank_options(dilemma) do |option|
31
+ option.fetch(:foundations, []).count { |fid| CARE_FOUNDATIONS.include?(fid) }.to_f
32
+ end
33
+ end
34
+
35
+ def evaluate_by_care(dilemma)
36
+ rank_options(dilemma) do |option|
37
+ option.fetch(:foundations, []).count { |fid| fid == :care }.to_f
38
+ end
39
+ end
40
+
41
+ def evaluate_by_justice(dilemma)
42
+ rank_options(dilemma) do |option|
43
+ option.fetch(:foundations, []).count { |fid| JUSTICE_FOUNDATIONS.include?(fid) }.to_f
44
+ end
45
+ end
46
+
47
+ def evaluate_by_rights(dilemma)
48
+ rank_options(dilemma) do |option|
49
+ option.fetch(:foundations, []).count { |fid| RIGHTS_FOUNDATIONS.include?(fid) }.to_f
50
+ end
51
+ end
52
+
53
+ def rank_options(dilemma)
54
+ scored = dilemma.options.map do |option|
55
+ score = yield(option)
56
+ { id: option[:id], description: option[:description], score: score }
57
+ end
58
+ scored.sort_by { |r| -r[:score] }
59
+ end
60
+ end
61
+
62
+ class MoralEngine
63
+ include Constants
64
+ include FrameworkEvaluators
65
+
66
+ KOHLBERG_STAGE_DESCRIPTIONS = {
67
+ obedience: 'Avoid punishment; obey authority unconditionally',
68
+ self_interest: 'Act for direct reward; reciprocal exchange',
69
+ conformity: 'Conform to social norms; be a good person',
70
+ law_and_order: 'Follow rules, laws, and authority to maintain social order',
71
+ social_contract: 'Uphold democratic principles; greatest good for greatest number',
72
+ universal_ethics: 'Follow self-chosen universal ethical principles'
73
+ }.freeze
74
+
75
+ FRAMEWORK_STRATEGIES = {
76
+ utilitarian: :evaluate_by_utility,
77
+ deontological: :evaluate_by_duty,
78
+ virtue: :evaluate_by_virtue,
79
+ care: :evaluate_by_care,
80
+ justice: :evaluate_by_justice,
81
+ rights: :evaluate_by_rights
82
+ }.freeze
83
+
84
+ attr_reader :stage, :dilemmas, :principles, :history
85
+
86
+ def initialize
87
+ @foundations = MORAL_FOUNDATIONS.to_h { |f| [f, MoralFoundation.new(id: f)] }
88
+ @principles = []
89
+ @dilemmas = {}
90
+ @stage = :social_contract
91
+ @history = []
92
+ end
93
+
94
+ def evaluate_action(action:, affected_foundations:, domain: :general)
95
+ score = score_foundations(affected_foundations)
96
+ normalized = affected_foundations.empty? ? 0.0 : score / affected_foundations.size
97
+ add_history(type: :evaluation, action: action, domain: domain, score: normalized)
98
+ { action: action, domain: domain, score: normalized, foundations: affected_foundations }
99
+ end
100
+
101
+ def pose_dilemma(description:, options:, domain: :general, severity: 0.5)
102
+ return { success: false, reason: :max_dilemmas_reached } if @dilemmas.size >= MAX_DILEMMAS
103
+
104
+ id = generate_id('dilemma')
105
+ dilemma = Dilemma.new(id: id, description: description, options: options,
106
+ domain: domain, severity: severity)
107
+ @dilemmas[id] = dilemma
108
+ { success: true, dilemma: dilemma.to_h }
109
+ end
110
+
111
+ def resolve_dilemma(dilemma_id:, option_id:, reasoning:, framework:)
112
+ dilemma = @dilemmas[dilemma_id]
113
+ return { success: false, reason: :not_found } unless dilemma
114
+ return { success: false, reason: :already_resolved } if dilemma.resolved?
115
+
116
+ chosen = dilemma.options.find { |o| o[:id] == option_id }
117
+ return { success: false, reason: :invalid_option } unless chosen
118
+
119
+ dilemma.resolve(option_id: option_id, reasoning: reasoning, framework: framework)
120
+ reinforce_chosen_foundations(chosen)
121
+ weaken_unchosen_foundations(dilemma.options, option_id)
122
+ add_history(type: :resolution, dilemma_id: dilemma_id, option_id: option_id,
123
+ framework: framework, severity: dilemma.severity)
124
+ { success: true, dilemma: dilemma.to_h }
125
+ end
126
+
127
+ def apply_framework(dilemma_id:, framework:)
128
+ dilemma = @dilemmas[dilemma_id]
129
+ return { success: false, reason: :not_found } unless dilemma
130
+ return { success: false, reason: :unknown_framework } unless ETHICAL_FRAMEWORKS.include?(framework)
131
+
132
+ strategy = FRAMEWORK_STRATEGIES.fetch(framework)
133
+ rankings = send(strategy, dilemma)
134
+ { success: true, dilemma_id: dilemma_id, framework: framework, rankings: rankings }
135
+ end
136
+
137
+ def add_principle(name:, description:, foundation:, weight: DEFAULT_WEIGHT)
138
+ return { success: false, reason: :max_principles_reached } if @principles.size >= MAX_PRINCIPLES
139
+ return { success: false, reason: :unknown_foundation } unless MORAL_FOUNDATIONS.include?(foundation)
140
+
141
+ principle = {
142
+ id: generate_id('principle'),
143
+ name: name,
144
+ description: description,
145
+ foundation: foundation,
146
+ weight: weight.clamp(WEIGHT_FLOOR, WEIGHT_CEILING),
147
+ created_at: Time.now.utc
148
+ }
149
+ @principles << principle
150
+ { success: true, principle: principle }
151
+ end
152
+
153
+ def moral_development
154
+ resolved = resolved_dilemmas
155
+ return { advanced: false, stage: @stage, reason: :insufficient_resolutions } if resolved.size < 3
156
+
157
+ avg_severity = resolved.sum(&:severity) / resolved.size.to_f
158
+ complexity_met = avg_severity >= 0.4 && resolved.size >= 5
159
+ current_idx = KOHLBERG_STAGES.index(@stage)
160
+
161
+ if complexity_met && current_idx < KOHLBERG_STAGES.size - 1
162
+ @stage = KOHLBERG_STAGES[current_idx + 1]
163
+ { advanced: true, stage: @stage, previous_stage: KOHLBERG_STAGES[current_idx] }
164
+ else
165
+ { advanced: false, stage: @stage, reason: :complexity_threshold_not_met }
166
+ end
167
+ end
168
+
169
+ def foundation_profile
170
+ @foundations.transform_values(&:to_h)
171
+ end
172
+
173
+ def stage_info
174
+ level = KOHLBERG_LEVELS.find { |_, stages| stages.include?(@stage) }&.first
175
+ { stage: @stage, level: level, description: KOHLBERG_STAGE_DESCRIPTIONS.fetch(@stage, 'Unknown') }
176
+ end
177
+
178
+ def unresolved_dilemmas
179
+ @dilemmas.values.reject(&:resolved?)
180
+ end
181
+
182
+ def resolved_dilemmas
183
+ @dilemmas.values.select(&:resolved?)
184
+ end
185
+
186
+ def decay_all
187
+ @foundations.each_value(&:decay)
188
+ end
189
+
190
+ def to_h
191
+ {
192
+ stage: @stage,
193
+ total_dilemmas: @dilemmas.size,
194
+ resolved_dilemmas: resolved_dilemmas.size,
195
+ unresolved_dilemmas: unresolved_dilemmas.size,
196
+ principles: @principles.size,
197
+ history_entries: @history.size,
198
+ foundation_profile: foundation_profile
199
+ }
200
+ end
201
+
202
+ private
203
+
204
+ def score_foundations(affected_foundations)
205
+ affected_foundations.sum do |fid|
206
+ foundation = @foundations.fetch(fid, nil)
207
+ next 0.0 unless foundation
208
+
209
+ foundation.weight * foundation.sensitivity
210
+ end
211
+ end
212
+
213
+ def reinforce_chosen_foundations(chosen_option)
214
+ chosen_option.fetch(:foundations, []).each do |fid|
215
+ @foundations[fid]&.reinforce(amount: chosen_option.fetch(:severity, 1.0))
216
+ end
217
+ end
218
+
219
+ def weaken_unchosen_foundations(options, chosen_id)
220
+ options.reject { |o| o[:id] == chosen_id }.each do |option|
221
+ option.fetch(:foundations, []).each { |fid| @foundations[fid]&.weaken(amount: 0.5) }
222
+ end
223
+ end
224
+
225
+ def add_history(entry)
226
+ @history << entry.merge(timestamp: Time.now.utc)
227
+ @history.shift if @history.size > MAX_HISTORY
228
+ end
229
+
230
+ def generate_id(prefix)
231
+ "#{prefix}_#{Time.now.utc.to_f}_#{rand(1000)}"
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module MoralReasoning
8
+ module Helpers
9
+ class MoralFoundation
10
+ include Constants
11
+
12
+ attr_reader :id, :weight, :sensitivity
13
+
14
+ def initialize(id:, weight: DEFAULT_WEIGHT, sensitivity: DEFAULT_WEIGHT)
15
+ @id = id
16
+ @weight = weight.clamp(WEIGHT_FLOOR, WEIGHT_CEILING)
17
+ @sensitivity = sensitivity.clamp(0.0, 1.0)
18
+ end
19
+
20
+ def reinforce(amount: 1.0)
21
+ @weight = (@weight + (amount * REINFORCEMENT_RATE)).clamp(WEIGHT_FLOOR, WEIGHT_CEILING)
22
+ end
23
+
24
+ def weaken(amount: 1.0)
25
+ @weight = (@weight - (amount * REINFORCEMENT_RATE)).clamp(WEIGHT_FLOOR, WEIGHT_CEILING)
26
+ end
27
+
28
+ def decay
29
+ @weight = (@weight - DECAY_RATE).clamp(WEIGHT_FLOOR, WEIGHT_CEILING)
30
+ end
31
+
32
+ def to_h
33
+ {
34
+ id: @id,
35
+ weight: @weight,
36
+ sensitivity: @sensitivity
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module MoralReasoning
8
+ module Runners
9
+ module MoralReasoning
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def evaluate_moral_action(action:, affected_foundations:, domain: :general, description: nil, **)
14
+ Legion::Logging.debug "[moral_reasoning] evaluate_action: action=#{action} domain=#{domain}"
15
+
16
+ if Helpers::LlmEnhancer.available?
17
+ current_foundations = engine.foundation_profile.transform_values { |f| f[:weight] }
18
+ llm_result = Helpers::LlmEnhancer.evaluate_action(
19
+ action: action,
20
+ description: description.to_s,
21
+ foundations: current_foundations
22
+ )
23
+ if llm_result
24
+ Legion::Logging.debug "[moral_reasoning] using LLM evaluation for action=#{action}"
25
+ result = engine.evaluate_action(
26
+ action: action,
27
+ affected_foundations: affected_foundations,
28
+ domain: domain
29
+ )
30
+ return { success: true, source: :llm, reasoning: llm_result[:reasoning],
31
+ foundation_impacts: llm_result[:foundation_impacts] }.merge(result)
32
+ end
33
+ end
34
+
35
+ result = engine.evaluate_action(action: action, affected_foundations: affected_foundations, domain: domain)
36
+ { success: true, source: :mechanical }.merge(result)
37
+ end
38
+
39
+ def pose_moral_dilemma(description:, options:, domain: :general, severity: 0.5, **)
40
+ Legion::Logging.info "[moral_reasoning] pose_dilemma: domain=#{domain} severity=#{severity}"
41
+ engine.pose_dilemma(description: description, options: options, domain: domain, severity: severity)
42
+ end
43
+
44
+ def resolve_moral_dilemma(dilemma_id:, option_id:, reasoning:, framework:, **)
45
+ Legion::Logging.info "[moral_reasoning] resolve_dilemma: id=#{dilemma_id} framework=#{framework}"
46
+
47
+ if Helpers::LlmEnhancer.available?
48
+ dilemma = engine.dilemmas[dilemma_id]
49
+ if dilemma && !dilemma.resolved?
50
+ llm_result = Helpers::LlmEnhancer.resolve_dilemma(
51
+ dilemma_description: dilemma.description,
52
+ options: dilemma.options,
53
+ framework: framework
54
+ )
55
+ if llm_result
56
+ Legion::Logging.debug "[moral_reasoning] using LLM resolution for dilemma=#{dilemma_id}"
57
+ result = engine.resolve_dilemma(
58
+ dilemma_id: dilemma_id,
59
+ option_id: option_id,
60
+ reasoning: llm_result[:reasoning],
61
+ framework: framework
62
+ )
63
+ return result.merge(source: :llm, llm_chosen: llm_result[:chosen_option],
64
+ llm_confidence: llm_result[:confidence])
65
+ end
66
+ end
67
+ end
68
+
69
+ engine.resolve_dilemma(dilemma_id: dilemma_id, option_id: option_id,
70
+ reasoning: reasoning, framework: framework)
71
+ end
72
+
73
+ def apply_ethical_framework(dilemma_id:, framework:, **)
74
+ Legion::Logging.debug "[moral_reasoning] apply_framework: id=#{dilemma_id} framework=#{framework}"
75
+ engine.apply_framework(dilemma_id: dilemma_id, framework: framework)
76
+ end
77
+
78
+ def add_moral_principle(name:, description:, foundation:, weight: Helpers::Constants::DEFAULT_WEIGHT, **)
79
+ Legion::Logging.info "[moral_reasoning] add_principle: name=#{name} foundation=#{foundation}"
80
+ engine.add_principle(name: name, description: description, foundation: foundation, weight: weight)
81
+ end
82
+
83
+ def check_moral_development(**)
84
+ Legion::Logging.debug '[moral_reasoning] check_moral_development'
85
+ result = engine.moral_development
86
+ { success: true }.merge(result)
87
+ end
88
+
89
+ def moral_foundation_profile(**)
90
+ Legion::Logging.debug '[moral_reasoning] foundation_profile'
91
+ { success: true, foundations: engine.foundation_profile }
92
+ end
93
+
94
+ def moral_stage_info(**)
95
+ Legion::Logging.debug '[moral_reasoning] stage_info'
96
+ { success: true }.merge(engine.stage_info)
97
+ end
98
+
99
+ def update_moral_reasoning(**)
100
+ Legion::Logging.debug '[moral_reasoning] decay_all'
101
+ engine.decay_all
102
+ { success: true, foundations: engine.foundation_profile }
103
+ end
104
+
105
+ def moral_reasoning_stats(**)
106
+ Legion::Logging.debug '[moral_reasoning] stats'
107
+ { success: true }.merge(engine.to_h)
108
+ end
109
+
110
+ private
111
+
112
+ def engine
113
+ @engine ||= Helpers::MoralEngine.new
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module MoralReasoning
8
+ VERSION = '0.1.1'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'moral_reasoning/version'
4
+ require_relative 'moral_reasoning/helpers/constants'
5
+ require_relative 'moral_reasoning/helpers/moral_foundation'
6
+ require_relative 'moral_reasoning/helpers/dilemma'
7
+ require_relative 'moral_reasoning/helpers/moral_engine'
8
+ require_relative 'moral_reasoning/helpers/llm_enhancer'
9
+ require_relative 'moral_reasoning/runners/moral_reasoning'
10
+ require_relative 'moral_reasoning/client'
11
+
12
+ module Legion
13
+ module Extensions
14
+ module Agentic
15
+ module Social
16
+ module MoralReasoning
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/social/perspective_shifting/helpers/constants'
4
+ require 'legion/extensions/agentic/social/perspective_shifting/helpers/perspective'
5
+ require 'legion/extensions/agentic/social/perspective_shifting/helpers/perspective_view'
6
+ require 'legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine'
7
+ require 'legion/extensions/agentic/social/perspective_shifting/runners/perspective_shifting'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module Agentic
12
+ module Social
13
+ module PerspectiveShifting
14
+ class Client
15
+ include Runners::PerspectiveShifting
16
+
17
+ def initialize(**)
18
+ @shifting_engine = Helpers::ShiftingEngine.new
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :shifting_engine
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module PerspectiveShifting
8
+ module Helpers
9
+ module Constants
10
+ MAX_PERSPECTIVES = 50
11
+ MAX_SITUATIONS = 200
12
+ MAX_VIEWS_PER_SITUATION = 20
13
+ DEFAULT_EMPATHY = 0.5
14
+ MIN_PERSPECTIVES_FOR_SYNTHESIS = 2
15
+
16
+ PERSPECTIVE_TYPES = %i[
17
+ stakeholder emotional temporal cultural ethical pragmatic creative adversarial
18
+ ].freeze
19
+
20
+ PRIORITY_TYPES = %i[
21
+ safety efficiency fairness innovation stability growth autonomy compliance
22
+ ].freeze
23
+
24
+ EMPATHY_LABELS = {
25
+ (0.8..) => :deeply_empathic,
26
+ (0.6...0.8) => :empathic,
27
+ (0.4...0.6) => :moderate,
28
+ (0.2...0.4) => :limited,
29
+ (..0.2) => :detached
30
+ }.freeze
31
+
32
+ COVERAGE_LABELS = {
33
+ (0.8..) => :comprehensive,
34
+ (0.6...0.8) => :thorough,
35
+ (0.4...0.6) => :partial,
36
+ (0.2...0.4) => :narrow,
37
+ (..0.2) => :blind
38
+ }.freeze
39
+
40
+ AGREEMENT_LABELS = {
41
+ (0.8..) => :consensus,
42
+ (0.6...0.8) => :agreement,
43
+ (0.4...0.6) => :mixed,
44
+ (0.2...0.4) => :disagreement,
45
+ (..0.2) => :conflict
46
+ }.freeze
47
+
48
+ module_function
49
+
50
+ def empathy_label(value)
51
+ EMPATHY_LABELS.find { |range, _| range.include?(value) }&.last || :detached
52
+ end
53
+
54
+ def coverage_label(value)
55
+ COVERAGE_LABELS.find { |range, _| range.include?(value) }&.last || :blind
56
+ end
57
+
58
+ def agreement_label(value)
59
+ AGREEMENT_LABELS.find { |range, _| range.include?(value) }&.last || :conflict
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end