lex-agentic-learning 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 (192) 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-learning.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/learning/anchoring/client.rb +26 -0
  8. data/lib/legion/extensions/agentic/learning/anchoring/helpers/anchor.rb +65 -0
  9. data/lib/legion/extensions/agentic/learning/anchoring/helpers/anchor_store.rb +132 -0
  10. data/lib/legion/extensions/agentic/learning/anchoring/helpers/constants.rb +31 -0
  11. data/lib/legion/extensions/agentic/learning/anchoring/runners/anchoring.rb +100 -0
  12. data/lib/legion/extensions/agentic/learning/anchoring/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/learning/anchoring.rb +19 -0
  14. data/lib/legion/extensions/agentic/learning/catalyst/client.rb +15 -0
  15. data/lib/legion/extensions/agentic/learning/catalyst/helpers/catalyst.rb +87 -0
  16. data/lib/legion/extensions/agentic/learning/catalyst/helpers/catalyst_engine.rb +153 -0
  17. data/lib/legion/extensions/agentic/learning/catalyst/helpers/constants.rb +55 -0
  18. data/lib/legion/extensions/agentic/learning/catalyst/helpers/reaction.rb +87 -0
  19. data/lib/legion/extensions/agentic/learning/catalyst/runners/cognitive_catalyst.rb +103 -0
  20. data/lib/legion/extensions/agentic/learning/catalyst/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/learning/catalyst.rb +22 -0
  22. data/lib/legion/extensions/agentic/learning/chrysalis/client.rb +22 -0
  23. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/chrysalis.rb +137 -0
  24. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/cocoon.rb +89 -0
  25. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/constants.rb +49 -0
  26. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/metamorphosis_engine.rb +157 -0
  27. data/lib/legion/extensions/agentic/learning/chrysalis/runners/cognitive_chrysalis.rb +129 -0
  28. data/lib/legion/extensions/agentic/learning/chrysalis/version.rb +13 -0
  29. data/lib/legion/extensions/agentic/learning/chrysalis.rb +21 -0
  30. data/lib/legion/extensions/agentic/learning/curiosity/client.rb +28 -0
  31. data/lib/legion/extensions/agentic/learning/curiosity/helpers/constants.rb +30 -0
  32. data/lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb +167 -0
  33. data/lib/legion/extensions/agentic/learning/curiosity/helpers/wonder.rb +73 -0
  34. data/lib/legion/extensions/agentic/learning/curiosity/helpers/wonder_store.rb +149 -0
  35. data/lib/legion/extensions/agentic/learning/curiosity/runners/curiosity.rb +163 -0
  36. data/lib/legion/extensions/agentic/learning/curiosity/version.rb +13 -0
  37. data/lib/legion/extensions/agentic/learning/curiosity.rb +21 -0
  38. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/client.rb +28 -0
  39. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/helpers/constants.rb +31 -0
  40. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/helpers/curiosity_engine.rb +122 -0
  41. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/helpers/knowledge_gap.rb +70 -0
  42. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/runners/epistemic_curiosity.rb +106 -0
  43. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/version.rb +13 -0
  44. data/lib/legion/extensions/agentic/learning/epistemic_curiosity.rb +19 -0
  45. data/lib/legion/extensions/agentic/learning/fermentation/client.rb +19 -0
  46. data/lib/legion/extensions/agentic/learning/fermentation/helpers/batch.rb +75 -0
  47. data/lib/legion/extensions/agentic/learning/fermentation/helpers/constants.rb +78 -0
  48. data/lib/legion/extensions/agentic/learning/fermentation/helpers/fermentation_engine.rb +147 -0
  49. data/lib/legion/extensions/agentic/learning/fermentation/helpers/substrate.rb +108 -0
  50. data/lib/legion/extensions/agentic/learning/fermentation/runners/cognitive_fermentation.rb +60 -0
  51. data/lib/legion/extensions/agentic/learning/fermentation/version.rb +13 -0
  52. data/lib/legion/extensions/agentic/learning/fermentation.rb +22 -0
  53. data/lib/legion/extensions/agentic/learning/habit/client.rb +26 -0
  54. data/lib/legion/extensions/agentic/learning/habit/helpers/action_sequence.rb +120 -0
  55. data/lib/legion/extensions/agentic/learning/habit/helpers/constants.rb +44 -0
  56. data/lib/legion/extensions/agentic/learning/habit/helpers/habit_store.rb +148 -0
  57. data/lib/legion/extensions/agentic/learning/habit/runners/habit.rb +86 -0
  58. data/lib/legion/extensions/agentic/learning/habit/version.rb +13 -0
  59. data/lib/legion/extensions/agentic/learning/habit.rb +19 -0
  60. data/lib/legion/extensions/agentic/learning/hebbian/actors/decay.rb +45 -0
  61. data/lib/legion/extensions/agentic/learning/hebbian/client.rb +29 -0
  62. data/lib/legion/extensions/agentic/learning/hebbian/helpers/assembly.rb +82 -0
  63. data/lib/legion/extensions/agentic/learning/hebbian/helpers/assembly_network.rb +190 -0
  64. data/lib/legion/extensions/agentic/learning/hebbian/helpers/constants.rb +50 -0
  65. data/lib/legion/extensions/agentic/learning/hebbian/helpers/unit.rb +94 -0
  66. data/lib/legion/extensions/agentic/learning/hebbian/runners/hebbian_assembly.rb +94 -0
  67. data/lib/legion/extensions/agentic/learning/hebbian/version.rb +13 -0
  68. data/lib/legion/extensions/agentic/learning/hebbian.rb +20 -0
  69. data/lib/legion/extensions/agentic/learning/learning_rate/client.rb +25 -0
  70. data/lib/legion/extensions/agentic/learning/learning_rate/helpers/constants.rb +35 -0
  71. data/lib/legion/extensions/agentic/learning/learning_rate/helpers/rate_model.rb +133 -0
  72. data/lib/legion/extensions/agentic/learning/learning_rate/runners/learning_rate.rb +85 -0
  73. data/lib/legion/extensions/agentic/learning/learning_rate/version.rb +13 -0
  74. data/lib/legion/extensions/agentic/learning/learning_rate.rb +18 -0
  75. data/lib/legion/extensions/agentic/learning/meta_learning/client.rb +27 -0
  76. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/constants.rb +46 -0
  77. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/learning_domain.rb +85 -0
  78. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/meta_learning_engine.rb +202 -0
  79. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/strategy.rb +62 -0
  80. data/lib/legion/extensions/agentic/learning/meta_learning/runners/meta_learning.rb +118 -0
  81. data/lib/legion/extensions/agentic/learning/meta_learning/version.rb +13 -0
  82. data/lib/legion/extensions/agentic/learning/meta_learning.rb +20 -0
  83. data/lib/legion/extensions/agentic/learning/plasticity/client.rb +15 -0
  84. data/lib/legion/extensions/agentic/learning/plasticity/helpers/constants.rb +45 -0
  85. data/lib/legion/extensions/agentic/learning/plasticity/helpers/neural_pathway.rb +85 -0
  86. data/lib/legion/extensions/agentic/learning/plasticity/helpers/plasticity_engine.rb +130 -0
  87. data/lib/legion/extensions/agentic/learning/plasticity/runners/cognitive_plasticity.rb +85 -0
  88. data/lib/legion/extensions/agentic/learning/plasticity/version.rb +13 -0
  89. data/lib/legion/extensions/agentic/learning/plasticity.rb +19 -0
  90. data/lib/legion/extensions/agentic/learning/preference_learning/actors/decay.rb +45 -0
  91. data/lib/legion/extensions/agentic/learning/preference_learning/client.rb +28 -0
  92. data/lib/legion/extensions/agentic/learning/preference_learning/helpers/constants.rb +35 -0
  93. data/lib/legion/extensions/agentic/learning/preference_learning/helpers/option.rb +78 -0
  94. data/lib/legion/extensions/agentic/learning/preference_learning/helpers/preference_engine.rb +121 -0
  95. data/lib/legion/extensions/agentic/learning/preference_learning/runners/preference_learning.rb +84 -0
  96. data/lib/legion/extensions/agentic/learning/preference_learning/version.rb +13 -0
  97. data/lib/legion/extensions/agentic/learning/preference_learning.rb +19 -0
  98. data/lib/legion/extensions/agentic/learning/procedural/client.rb +19 -0
  99. data/lib/legion/extensions/agentic/learning/procedural/helpers/constants.rb +46 -0
  100. data/lib/legion/extensions/agentic/learning/procedural/helpers/learning_engine.rb +160 -0
  101. data/lib/legion/extensions/agentic/learning/procedural/helpers/production.rb +66 -0
  102. data/lib/legion/extensions/agentic/learning/procedural/helpers/skill.rb +101 -0
  103. data/lib/legion/extensions/agentic/learning/procedural/runners/procedural_learning.rb +96 -0
  104. data/lib/legion/extensions/agentic/learning/procedural/version.rb +13 -0
  105. data/lib/legion/extensions/agentic/learning/procedural.rb +20 -0
  106. data/lib/legion/extensions/agentic/learning/scaffolding/client.rb +26 -0
  107. data/lib/legion/extensions/agentic/learning/scaffolding/helpers/constants.rb +42 -0
  108. data/lib/legion/extensions/agentic/learning/scaffolding/helpers/scaffold.rb +136 -0
  109. data/lib/legion/extensions/agentic/learning/scaffolding/helpers/scaffolding_engine.rb +112 -0
  110. data/lib/legion/extensions/agentic/learning/scaffolding/runners/cognitive_scaffolding.rb +107 -0
  111. data/lib/legion/extensions/agentic/learning/scaffolding/version.rb +13 -0
  112. data/lib/legion/extensions/agentic/learning/scaffolding.rb +19 -0
  113. data/lib/legion/extensions/agentic/learning/version.rb +11 -0
  114. data/lib/legion/extensions/agentic/learning.rb +31 -0
  115. data/spec/legion/extensions/agentic/learning/anchoring/client_spec.rb +32 -0
  116. data/spec/legion/extensions/agentic/learning/anchoring/helpers/anchor_spec.rb +130 -0
  117. data/spec/legion/extensions/agentic/learning/anchoring/helpers/anchor_store_spec.rb +201 -0
  118. data/spec/legion/extensions/agentic/learning/anchoring/helpers/constants_spec.rb +63 -0
  119. data/spec/legion/extensions/agentic/learning/anchoring/runners/anchoring_spec.rb +199 -0
  120. data/spec/legion/extensions/agentic/learning/catalyst/client_spec.rb +58 -0
  121. data/spec/legion/extensions/agentic/learning/catalyst/cognitive_catalyst_spec.rb +49 -0
  122. data/spec/legion/extensions/agentic/learning/catalyst/helpers/catalyst_engine_spec.rb +263 -0
  123. data/spec/legion/extensions/agentic/learning/catalyst/helpers/catalyst_spec.rb +214 -0
  124. data/spec/legion/extensions/agentic/learning/catalyst/helpers/reaction_spec.rb +223 -0
  125. data/spec/legion/extensions/agentic/learning/catalyst/runners/cognitive_catalyst_spec.rb +217 -0
  126. data/spec/legion/extensions/agentic/learning/chrysalis/client_spec.rb +83 -0
  127. data/spec/legion/extensions/agentic/learning/chrysalis/cognitive_chrysalis_spec.rb +15 -0
  128. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/chrysalis_engine_spec.rb +57 -0
  129. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/chrysalis_spec.rb +305 -0
  130. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/cocoon_spec.rb +206 -0
  131. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/constants_spec.rb +109 -0
  132. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/metamorphic_cycle_spec.rb +76 -0
  133. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/metamorphosis_engine_spec.rb +247 -0
  134. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/transformation_phase_spec.rb +98 -0
  135. data/spec/legion/extensions/agentic/learning/chrysalis/runners/cognitive_chrysalis_spec.rb +180 -0
  136. data/spec/legion/extensions/agentic/learning/chrysalis/runners/reporting_spec.rb +81 -0
  137. data/spec/legion/extensions/agentic/learning/chrysalis/runners/transformation_spec.rb +74 -0
  138. data/spec/legion/extensions/agentic/learning/curiosity/client_spec.rb +27 -0
  139. data/spec/legion/extensions/agentic/learning/curiosity/helpers/gap_detector_spec.rb +118 -0
  140. data/spec/legion/extensions/agentic/learning/curiosity/helpers/wonder_spec.rb +130 -0
  141. data/spec/legion/extensions/agentic/learning/curiosity/helpers/wonder_store_spec.rb +136 -0
  142. data/spec/legion/extensions/agentic/learning/curiosity/runners/curiosity_spec.rb +159 -0
  143. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/client_spec.rb +47 -0
  144. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/helpers/constants_spec.rb +45 -0
  145. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/helpers/curiosity_engine_spec.rb +229 -0
  146. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/helpers/knowledge_gap_spec.rb +188 -0
  147. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/runners/epistemic_curiosity_spec.rb +175 -0
  148. data/spec/legion/extensions/agentic/learning/fermentation/client_spec.rb +36 -0
  149. data/spec/legion/extensions/agentic/learning/fermentation/helpers/batch_spec.rb +72 -0
  150. data/spec/legion/extensions/agentic/learning/fermentation/helpers/fermentation_engine_spec.rb +138 -0
  151. data/spec/legion/extensions/agentic/learning/fermentation/helpers/substrate_spec.rb +146 -0
  152. data/spec/legion/extensions/agentic/learning/habit/client_spec.rb +50 -0
  153. data/spec/legion/extensions/agentic/learning/habit/helpers/action_sequence_spec.rb +276 -0
  154. data/spec/legion/extensions/agentic/learning/habit/helpers/constants_spec.rb +115 -0
  155. data/spec/legion/extensions/agentic/learning/habit/helpers/habit_store_spec.rb +274 -0
  156. data/spec/legion/extensions/agentic/learning/habit/runners/habit_spec.rb +228 -0
  157. data/spec/legion/extensions/agentic/learning/hebbian/client_spec.rb +38 -0
  158. data/spec/legion/extensions/agentic/learning/hebbian/helpers/assembly_network_spec.rb +142 -0
  159. data/spec/legion/extensions/agentic/learning/hebbian/helpers/assembly_spec.rb +89 -0
  160. data/spec/legion/extensions/agentic/learning/hebbian/helpers/unit_spec.rb +119 -0
  161. data/spec/legion/extensions/agentic/learning/hebbian/runners/hebbian_assembly_spec.rb +109 -0
  162. data/spec/legion/extensions/agentic/learning/learning_rate/client_spec.rb +51 -0
  163. data/spec/legion/extensions/agentic/learning/learning_rate/helpers/constants_spec.rb +29 -0
  164. data/spec/legion/extensions/agentic/learning/learning_rate/helpers/rate_model_spec.rb +151 -0
  165. data/spec/legion/extensions/agentic/learning/learning_rate/runners/learning_rate_spec.rb +92 -0
  166. data/spec/legion/extensions/agentic/learning/meta_learning/client_spec.rb +27 -0
  167. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/constants_spec.rb +43 -0
  168. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/learning_domain_spec.rb +146 -0
  169. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/meta_learning_engine_spec.rb +309 -0
  170. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/strategy_spec.rb +82 -0
  171. data/spec/legion/extensions/agentic/learning/meta_learning/runners/meta_learning_spec.rb +185 -0
  172. data/spec/legion/extensions/agentic/learning/plasticity/helpers/constants_spec.rb +54 -0
  173. data/spec/legion/extensions/agentic/learning/plasticity/helpers/neural_pathway_spec.rb +136 -0
  174. data/spec/legion/extensions/agentic/learning/plasticity/helpers/plasticity_engine_spec.rb +157 -0
  175. data/spec/legion/extensions/agentic/learning/plasticity/runners/cognitive_plasticity_spec.rb +83 -0
  176. data/spec/legion/extensions/agentic/learning/preference_learning/client_spec.rb +17 -0
  177. data/spec/legion/extensions/agentic/learning/preference_learning/helpers/constants_spec.rb +67 -0
  178. data/spec/legion/extensions/agentic/learning/preference_learning/helpers/option_spec.rb +104 -0
  179. data/spec/legion/extensions/agentic/learning/preference_learning/helpers/preference_engine_spec.rb +151 -0
  180. data/spec/legion/extensions/agentic/learning/preference_learning/runners/preference_learning_spec.rb +86 -0
  181. data/spec/legion/extensions/agentic/learning/procedural/client_spec.rb +22 -0
  182. data/spec/legion/extensions/agentic/learning/procedural/helpers/learning_engine_spec.rb +135 -0
  183. data/spec/legion/extensions/agentic/learning/procedural/helpers/production_spec.rb +66 -0
  184. data/spec/legion/extensions/agentic/learning/procedural/helpers/skill_spec.rb +102 -0
  185. data/spec/legion/extensions/agentic/learning/procedural/runners/procedural_learning_spec.rb +94 -0
  186. data/spec/legion/extensions/agentic/learning/scaffolding/client_spec.rb +20 -0
  187. data/spec/legion/extensions/agentic/learning/scaffolding/helpers/constants_spec.rb +36 -0
  188. data/spec/legion/extensions/agentic/learning/scaffolding/helpers/scaffold_spec.rb +187 -0
  189. data/spec/legion/extensions/agentic/learning/scaffolding/helpers/scaffolding_engine_spec.rb +159 -0
  190. data/spec/legion/extensions/agentic/learning/scaffolding/runners/cognitive_scaffolding_spec.rb +163 -0
  191. data/spec/spec_helper.rb +46 -0
  192. metadata +277 -0
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Learning
7
+ module Curiosity
8
+ module Helpers
9
+ # In-memory priority queue for wonder items with domain balancing and decay.
10
+ class WonderStore
11
+ attr_reader :resolved_count, :total_generated
12
+
13
+ def initialize
14
+ @wonders = {}
15
+ @resolved_count = 0
16
+ @total_generated = 0
17
+ @domain_resolution_rates = Hash.new { |h, k| h[k] = { resolved: 0, total: 0 } }
18
+ end
19
+
20
+ def store(wonder)
21
+ prune_if_full
22
+ @wonders[wonder[:wonder_id]] = wonder
23
+ @total_generated += 1
24
+ @domain_resolution_rates[wonder[:domain]][:total] += 1
25
+ wonder
26
+ end
27
+
28
+ def get(wonder_id)
29
+ @wonders[wonder_id]
30
+ end
31
+
32
+ def update(wonder_id, attrs)
33
+ wonder = @wonders[wonder_id]
34
+ return nil unless wonder
35
+
36
+ @wonders[wonder_id] = wonder.merge(attrs)
37
+ end
38
+
39
+ def delete(wonder_id)
40
+ @wonders.delete(wonder_id)
41
+ end
42
+
43
+ def active_wonders
44
+ @wonders.values.reject { |w| w[:resolved] }
45
+ end
46
+
47
+ def resolved_wonders
48
+ @wonders.values.select { |w| w[:resolved] }
49
+ end
50
+
51
+ def top(limit: 5, exclude_domains: [])
52
+ active_wonders
53
+ .reject { |w| exclude_domains.include?(w[:domain]) }
54
+ .sort_by { |w| -Wonder.score(w) }
55
+ .first(limit)
56
+ end
57
+
58
+ def top_balanced(limit: 5)
59
+ domain_counts = active_wonders.group_by { |w| w[:domain] }
60
+ .transform_values(&:size)
61
+ max_domain = domain_counts.values.max || 1
62
+
63
+ active_wonders
64
+ .sort_by do |w|
65
+ domain_penalty = (domain_counts[w[:domain]].to_f / max_domain) * Constants::DOMAIN_BALANCE_FACTOR
66
+ -(Wonder.score(w) - domain_penalty)
67
+ end
68
+ .first(limit)
69
+ end
70
+
71
+ def by_domain(domain)
72
+ active_wonders.select { |w| w[:domain] == domain.to_sym }
73
+ end
74
+
75
+ def mark_resolved(wonder_id, resolution:, actual_gain: 0.5)
76
+ wonder = @wonders[wonder_id]
77
+ return nil unless wonder
78
+
79
+ @wonders[wonder_id] = wonder.merge(
80
+ resolved: true,
81
+ resolution: resolution,
82
+ actual_gain: actual_gain
83
+ )
84
+ @resolved_count += 1
85
+ @domain_resolution_rates[wonder[:domain]][:resolved] += 1
86
+ @wonders[wonder_id]
87
+ end
88
+
89
+ def decay_all(hours_elapsed: 1.0)
90
+ pruned = 0
91
+ @wonders.each do |id, wonder|
92
+ next if wonder[:resolved]
93
+
94
+ decayed = Wonder.decay_salience(wonder, hours_elapsed: hours_elapsed)
95
+ if decayed[:salience] <= 0.0 || Wonder.stale?(decayed)
96
+ @wonders.delete(id)
97
+ pruned += 1
98
+ else
99
+ @wonders[id] = decayed
100
+ end
101
+ end
102
+ pruned
103
+ end
104
+
105
+ def count
106
+ @wonders.size
107
+ end
108
+
109
+ def active_count
110
+ active_wonders.size
111
+ end
112
+
113
+ def domain_stats
114
+ stats = Hash.new { |h, k| h[k] = { active: 0, resolved: 0 } }
115
+ @wonders.each_value do |w|
116
+ key = w[:resolved] ? :resolved : :active
117
+ stats[w[:domain]][key] += 1
118
+ end
119
+ stats
120
+ end
121
+
122
+ def resolution_rate
123
+ return 0.0 if @total_generated.zero?
124
+
125
+ @resolved_count.to_f / @total_generated
126
+ end
127
+
128
+ def domain_resolution_rate(domain)
129
+ rates = @domain_resolution_rates[domain.to_sym]
130
+ return 0.0 if rates[:total].zero?
131
+
132
+ rates[:resolved].to_f / rates[:total]
133
+ end
134
+
135
+ private
136
+
137
+ def prune_if_full
138
+ return unless active_count >= Constants::MAX_WONDERS
139
+
140
+ lowest = active_wonders.min_by { |w| Wonder.score(w) }
141
+ @wonders.delete(lowest[:wonder_id]) if lowest
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Learning
7
+ module Curiosity
8
+ module Runners
9
+ # Runner methods for the curiosity engine: gap detection, wonder lifecycle, agenda formation.
10
+ module Curiosity
11
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
12
+ Legion::Extensions::Helpers.const_defined?(:Lex)
13
+
14
+ def detect_gaps(prior_results: {}, **)
15
+ gaps = Helpers::GapDetector.detect(prior_results)
16
+ Legion::Logging.debug "[curiosity] detected #{gaps.size} knowledge gaps"
17
+
18
+ created = create_wonders_from_gaps(gaps)
19
+ build_detect_result(gaps, created)
20
+ end
21
+
22
+ def generate_wonder(question:, domain: :general, gap_type: :unknown,
23
+ salience: 0.5, information_gain: 0.5,
24
+ source_trace_ids: [], **)
25
+ wonder = Helpers::Wonder.new_wonder(
26
+ question: question, domain: domain, gap_type: gap_type,
27
+ salience: salience, information_gain: information_gain,
28
+ source_trace_ids: source_trace_ids
29
+ )
30
+ wonder_store.store(wonder)
31
+ Legion::Logging.info "[curiosity] manually generated wonder: #{question}"
32
+ wonder
33
+ end
34
+
35
+ def explore_wonder(wonder_id:, **)
36
+ wonder = wonder_store.get(wonder_id)
37
+ return { error: :not_found } unless wonder
38
+ return { error: :already_resolved } if wonder[:resolved]
39
+ return { error: :not_explorable, reason: :max_attempts } unless Helpers::Wonder.explorable?(wonder)
40
+
41
+ wonder_store.update(wonder_id, attempts: wonder[:attempts] + 1, last_explored_at: Time.now.utc)
42
+ Legion::Logging.info "[curiosity] exploring: #{wonder[:question]} (attempt ##{wonder[:attempts] + 1})"
43
+ { exploring: true, wonder_id: wonder_id, attempt: wonder[:attempts] + 1 }
44
+ end
45
+
46
+ def resolve_wonder(wonder_id:, resolution:, actual_gain: 0.5, **)
47
+ wonder = wonder_store.get(wonder_id)
48
+ return { error: :not_found } unless wonder
49
+ return { error: :already_resolved } if wonder[:resolved]
50
+
51
+ resolved = wonder_store.mark_resolved(wonder_id, resolution: resolution, actual_gain: actual_gain)
52
+ build_resolve_result(wonder, resolved, actual_gain)
53
+ end
54
+
55
+ def curiosity_intensity(**)
56
+ intensity = compute_intensity
57
+ Legion::Logging.debug "[curiosity] intensity=#{intensity.round(3)}"
58
+ { intensity: intensity, active_wonders: wonder_store.active_count,
59
+ resolution_rate: wonder_store.resolution_rate.round(3), top_domain: top_curiosity_domain }
60
+ end
61
+
62
+ def top_wonders(limit: 5, **)
63
+ wonders = wonder_store.top_balanced(limit: limit)
64
+ Legion::Logging.debug "[curiosity] top #{wonders.size} wonders requested"
65
+ { wonders: wonders.map { |w| format_wonder(w) } }
66
+ end
67
+
68
+ def form_agenda(**)
69
+ wonders = wonder_store.top_balanced(limit: 5)
70
+ Legion::Logging.debug "[curiosity] forming agenda from #{wonders.size} wonders"
71
+ { agenda_items: wonders.map { |w| format_agenda_item(w) }, source: :curiosity }
72
+ end
73
+
74
+ def wonder_stats(**)
75
+ { total_generated: wonder_store.total_generated, active: wonder_store.active_count,
76
+ resolved: wonder_store.resolved_count, resolution_rate: wonder_store.resolution_rate.round(3),
77
+ intensity: compute_intensity.round(3), domains: wonder_store.domain_stats }
78
+ end
79
+
80
+ def decay_wonders(hours_elapsed: 1.0, **)
81
+ pruned = wonder_store.decay_all(hours_elapsed: hours_elapsed)
82
+ Legion::Logging.debug "[curiosity] decay: pruned=#{pruned} remaining=#{wonder_store.active_count}"
83
+ { pruned: pruned, remaining: wonder_store.active_count }
84
+ end
85
+
86
+ private
87
+
88
+ def wonder_store
89
+ @wonder_store ||= Helpers::WonderStore.new
90
+ end
91
+
92
+ def create_wonders_from_gaps(gaps)
93
+ gaps.each_with_object([]) do |gap, created|
94
+ next if duplicate_wonder?(gap)
95
+
96
+ wonder = Helpers::Wonder.new_wonder(**gap.slice(:question, :domain, :gap_type,
97
+ :salience, :information_gain, :source_trace_ids))
98
+ wonder_store.store(wonder)
99
+ created << wonder
100
+ Legion::Logging.info "[curiosity] new wonder: #{wonder[:question]} (#{wonder[:gap_type]}/#{wonder[:domain]})"
101
+ end
102
+ end
103
+
104
+ def build_detect_result(gaps, created)
105
+ intensity = compute_intensity
106
+ top = wonder_store.top_balanced(limit: 3)
107
+ Legion::Logging.debug "[curiosity] intensity=#{intensity.round(3)} active=#{wonder_store.active_count}"
108
+ { gaps_detected: gaps.size, wonders_created: created.size, curiosity_intensity: intensity,
109
+ top_wonders: top.map { |w| { wonder_id: w[:wonder_id], question: w[:question], score: Helpers::Wonder.score(w).round(3) } },
110
+ active_count: wonder_store.active_count }
111
+ end
112
+
113
+ def build_resolve_result(wonder, resolved, actual_gain)
114
+ reward = actual_gain * Helpers::Constants::CURIOSITY_REWARD_MULTIPLIER
115
+ Legion::Logging.info "[curiosity] resolved: #{wonder[:question]} gain=#{actual_gain.round(2)}"
116
+ { resolved: true, wonder_id: wonder[:wonder_id], actual_gain: actual_gain,
117
+ expected_gain: wonder[:information_gain], reward: reward,
118
+ domain: resolved[:domain], resolution_rate: wonder_store.resolution_rate.round(3) }
119
+ end
120
+
121
+ def format_wonder(wonder)
122
+ { wonder_id: wonder[:wonder_id], question: wonder[:question], domain: wonder[:domain],
123
+ gap_type: wonder[:gap_type], score: Helpers::Wonder.score(wonder).round(3),
124
+ attempts: wonder[:attempts], explorable: Helpers::Wonder.explorable?(wonder),
125
+ created_at: wonder[:created_at] }
126
+ end
127
+
128
+ def format_agenda_item(wonder)
129
+ { type: :curious, source: :curiosity, weight: Helpers::Wonder.score(wonder),
130
+ summary: wonder[:question],
131
+ metadata: { wonder_id: wonder[:wonder_id], domain: wonder[:domain], gap_type: wonder[:gap_type] } }
132
+ end
133
+
134
+ def compute_intensity
135
+ active = wonder_store.active_count
136
+ return 0.0 if active.zero?
137
+
138
+ fullness = active.to_f / Helpers::Constants::MAX_WONDERS
139
+ avg_salience = wonder_store.active_wonders.sum { |w| w[:salience] } / active
140
+ explorable = wonder_store.active_wonders.count { |w| Helpers::Wonder.explorable?(w) }
141
+ ((fullness * 0.3) + (avg_salience * 0.4) + ((explorable.to_f / active) * 0.3)).clamp(0.0, 1.0)
142
+ end
143
+
144
+ def top_curiosity_domain
145
+ groups = wonder_store.active_wonders.group_by { |w| w[:domain] }
146
+ return nil if groups.empty?
147
+
148
+ groups.max_by { |_, wonders| wonders.sum { |w| Helpers::Wonder.score(w) } }&.first
149
+ end
150
+
151
+ def duplicate_wonder?(gap)
152
+ wonder_store.active_wonders.any? do |existing|
153
+ existing[:domain] == gap[:domain] && existing[:gap_type] == gap[:gap_type] &&
154
+ existing[:question] == gap[:question]
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Learning
7
+ module Curiosity
8
+ VERSION = '0.1.0'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/learning/curiosity/version'
4
+ require 'legion/extensions/agentic/learning/curiosity/helpers/constants'
5
+ require 'legion/extensions/agentic/learning/curiosity/helpers/wonder'
6
+ require 'legion/extensions/agentic/learning/curiosity/helpers/wonder_store'
7
+ require 'legion/extensions/agentic/learning/curiosity/helpers/gap_detector'
8
+ require 'legion/extensions/agentic/learning/curiosity/runners/curiosity'
9
+ require 'legion/extensions/agentic/learning/curiosity/client'
10
+
11
+ module Legion
12
+ module Extensions
13
+ # Intrinsic curiosity engine — knowledge gap detection, wonder lifecycle, curiosity-driven learning.
14
+ module Agentic
15
+ module Learning
16
+ module Curiosity
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/learning/epistemic_curiosity/helpers/constants'
4
+ require 'legion/extensions/agentic/learning/epistemic_curiosity/helpers/knowledge_gap'
5
+ require 'legion/extensions/agentic/learning/epistemic_curiosity/helpers/curiosity_engine'
6
+ require 'legion/extensions/agentic/learning/epistemic_curiosity/runners/epistemic_curiosity'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Agentic
11
+ module Learning
12
+ module EpistemicCuriosity
13
+ class Client
14
+ include Runners::EpistemicCuriosity
15
+
16
+ def initialize(**)
17
+ @engine = Helpers::CuriosityEngine.new
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :engine
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Learning
7
+ module EpistemicCuriosity
8
+ module Helpers
9
+ module Constants
10
+ MAX_GAPS = 300
11
+ DEFAULT_URGENCY = 0.5
12
+ URGENCY_BOOST = 0.08
13
+ URGENCY_DECAY = 0.03
14
+ SATISFACTION_THRESHOLD = 0.8
15
+
16
+ GAP_TYPES = %i[factual conceptual procedural causal relational].freeze
17
+
18
+ URGENCY_LABELS = [
19
+ { range: (0.8..1.0), label: :burning },
20
+ { range: (0.6...0.8), label: :intense },
21
+ { range: (0.4...0.6), label: :moderate },
22
+ { range: (0.2...0.4), label: :mild },
23
+ { range: (0.0...0.2), label: :satisfied }
24
+ ].freeze
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Learning
7
+ module EpistemicCuriosity
8
+ module Helpers
9
+ class CuriosityEngine
10
+ def initialize
11
+ @gaps = {}
12
+ @counter = 0
13
+ end
14
+
15
+ def create_gap(question:, domain:, gap_type: :factual, urgency: Constants::DEFAULT_URGENCY)
16
+ prune_resolved if @gaps.size >= Constants::MAX_GAPS
17
+ return { created: false, reason: :at_capacity } if @gaps.size >= Constants::MAX_GAPS
18
+
19
+ gap_type = :factual unless Constants::GAP_TYPES.include?(gap_type)
20
+ id = next_id
21
+ gap = KnowledgeGap.new(id: id, question: question, domain: domain, gap_type: gap_type, urgency: urgency)
22
+ @gaps[id] = gap
23
+ { created: true, gap: gap.to_h }
24
+ end
25
+
26
+ def explore_gap(gap_id:)
27
+ gap = @gaps[gap_id]
28
+ return { found: false, gap_id: gap_id } unless gap
29
+
30
+ gap.explore!
31
+ { found: true, gap: gap.to_h }
32
+ end
33
+
34
+ def satisfy_gap(gap_id:, amount: 0.3)
35
+ gap = @gaps[gap_id]
36
+ return { found: false, gap_id: gap_id } unless gap
37
+
38
+ gap.satisfy!(amount: amount)
39
+ { found: true, gap: gap.to_h }
40
+ end
41
+
42
+ def resolve_gap(gap_id:)
43
+ gap = @gaps[gap_id]
44
+ return { found: false, gap_id: gap_id } unless gap
45
+
46
+ gap.satisfaction = 1.0
47
+ gap.resolved_at = Time.now.utc
48
+ { found: true, resolved: true, gap: gap.to_h }
49
+ end
50
+
51
+ def open_gaps
52
+ @gaps.values.reject(&:resolved?)
53
+ end
54
+
55
+ def resolved_gaps
56
+ @gaps.values.select(&:resolved?)
57
+ end
58
+
59
+ def most_urgent(limit: 5)
60
+ open_gaps.sort_by { |g| -g.urgency }.first(limit)
61
+ end
62
+
63
+ def by_domain(domain)
64
+ @gaps.values.select { |g| g.domain == domain }
65
+ end
66
+
67
+ def by_type(gap_type)
68
+ @gaps.values.select { |g| g.gap_type == gap_type }
69
+ end
70
+
71
+ def information_debt
72
+ open_gaps.sum(&:urgency).round(4)
73
+ end
74
+
75
+ def exploration_efficiency
76
+ total = @gaps.size
77
+ return 0.0 if total.zero?
78
+
79
+ (resolved_gaps.size.to_f / total).round(4)
80
+ end
81
+
82
+ def decay_all
83
+ @gaps.each_value(&:decay!)
84
+ @gaps.size
85
+ end
86
+
87
+ def prune_resolved
88
+ @gaps.delete_if { |_id, gap| gap.resolved? }
89
+ end
90
+
91
+ def curiosity_report
92
+ {
93
+ total_gaps: @gaps.size,
94
+ open_gaps: open_gaps.size,
95
+ resolved_gaps: resolved_gaps.size,
96
+ information_debt: information_debt,
97
+ exploration_efficiency: exploration_efficiency,
98
+ most_urgent: most_urgent(limit: 3).map(&:to_h)
99
+ }
100
+ end
101
+
102
+ def to_h
103
+ {
104
+ gaps: @gaps.transform_values(&:to_h),
105
+ information_debt: information_debt,
106
+ exploration_efficiency: exploration_efficiency
107
+ }
108
+ end
109
+
110
+ private
111
+
112
+ def next_id
113
+ @counter += 1
114
+ :"gap_#{@counter}"
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Learning
7
+ module EpistemicCuriosity
8
+ module Helpers
9
+ class KnowledgeGap
10
+ attr_reader :id, :question, :domain, :gap_type, :explorations, :created_at
11
+ attr_accessor :urgency, :satisfaction, :resolved_at
12
+
13
+ def initialize(id:, question:, domain:, gap_type:, urgency: Constants::DEFAULT_URGENCY)
14
+ @id = id
15
+ @question = question
16
+ @domain = domain
17
+ @gap_type = gap_type
18
+ @urgency = urgency.clamp(0.0, 1.0)
19
+ @satisfaction = 0.0
20
+ @explorations = 0
21
+ @created_at = Time.now.utc
22
+ @resolved_at = nil
23
+ end
24
+
25
+ def explore!
26
+ @explorations += 1
27
+ @urgency = (@urgency + Constants::URGENCY_BOOST).clamp(0.0, 1.0)
28
+ self
29
+ end
30
+
31
+ def satisfy!(amount: 0.3)
32
+ @satisfaction = (@satisfaction + amount).clamp(0.0, 1.0)
33
+ self
34
+ end
35
+
36
+ def resolved?
37
+ @satisfaction >= Constants::SATISFACTION_THRESHOLD
38
+ end
39
+
40
+ def urgency_label
41
+ Constants::URGENCY_LABELS.find { |entry| entry[:range].cover?(@urgency) }&.fetch(:label, :mild) || :mild
42
+ end
43
+
44
+ def decay!
45
+ @urgency = (@urgency - Constants::URGENCY_DECAY).clamp(0.0, 1.0)
46
+ self
47
+ end
48
+
49
+ def to_h
50
+ {
51
+ id: @id,
52
+ question: @question,
53
+ domain: @domain,
54
+ gap_type: @gap_type,
55
+ urgency: @urgency.round(4),
56
+ urgency_label: urgency_label,
57
+ satisfaction: @satisfaction.round(4),
58
+ explorations: @explorations,
59
+ resolved: resolved?,
60
+ created_at: @created_at,
61
+ resolved_at: @resolved_at
62
+ }
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end