lex-agentic-memory 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 (267) 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-memory.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/memory/archaeology/client.rb +19 -0
  8. data/lib/legion/extensions/agentic/memory/archaeology/helpers/archaeology_engine.rb +184 -0
  9. data/lib/legion/extensions/agentic/memory/archaeology/helpers/artifact.rb +146 -0
  10. data/lib/legion/extensions/agentic/memory/archaeology/helpers/constants.rb +95 -0
  11. data/lib/legion/extensions/agentic/memory/archaeology/helpers/excavation_site.rb +97 -0
  12. data/lib/legion/extensions/agentic/memory/archaeology/runners/cognitive_archaeology.rb +86 -0
  13. data/lib/legion/extensions/agentic/memory/archaeology/version.rb +13 -0
  14. data/lib/legion/extensions/agentic/memory/archaeology.rb +22 -0
  15. data/lib/legion/extensions/agentic/memory/compression/client.rb +15 -0
  16. data/lib/legion/extensions/agentic/memory/compression/helpers/compression_engine.rb +145 -0
  17. data/lib/legion/extensions/agentic/memory/compression/helpers/constants.rb +44 -0
  18. data/lib/legion/extensions/agentic/memory/compression/helpers/information_chunk.rb +81 -0
  19. data/lib/legion/extensions/agentic/memory/compression/runners/cognitive_compression.rb +76 -0
  20. data/lib/legion/extensions/agentic/memory/compression/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/memory/compression.rb +19 -0
  22. data/lib/legion/extensions/agentic/memory/echo/client.rb +19 -0
  23. data/lib/legion/extensions/agentic/memory/echo/helpers/constants.rb +57 -0
  24. data/lib/legion/extensions/agentic/memory/echo/helpers/echo.rb +95 -0
  25. data/lib/legion/extensions/agentic/memory/echo/helpers/echo_engine.rb +136 -0
  26. data/lib/legion/extensions/agentic/memory/echo/runners/cognitive_echo.rb +81 -0
  27. data/lib/legion/extensions/agentic/memory/echo/version.rb +13 -0
  28. data/lib/legion/extensions/agentic/memory/echo.rb +19 -0
  29. data/lib/legion/extensions/agentic/memory/echo_chamber/client.rb +19 -0
  30. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/chamber.rb +134 -0
  31. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/chamber_engine.rb +156 -0
  32. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/constants.rb +60 -0
  33. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/echo.rb +96 -0
  34. data/lib/legion/extensions/agentic/memory/echo_chamber/runners/cognitive_echo_chamber.rb +84 -0
  35. data/lib/legion/extensions/agentic/memory/echo_chamber/version.rb +13 -0
  36. data/lib/legion/extensions/agentic/memory/echo_chamber.rb +21 -0
  37. data/lib/legion/extensions/agentic/memory/episodic/actors/decay.rb +45 -0
  38. data/lib/legion/extensions/agentic/memory/episodic/client.rb +25 -0
  39. data/lib/legion/extensions/agentic/memory/episodic/helpers/constants.rb +36 -0
  40. data/lib/legion/extensions/agentic/memory/episodic/helpers/episode.rb +104 -0
  41. data/lib/legion/extensions/agentic/memory/episodic/helpers/episodic_binding.rb +57 -0
  42. data/lib/legion/extensions/agentic/memory/episodic/helpers/episodic_store.rb +141 -0
  43. data/lib/legion/extensions/agentic/memory/episodic/runners/episodic_buffer.rb +108 -0
  44. data/lib/legion/extensions/agentic/memory/episodic/version.rb +13 -0
  45. data/lib/legion/extensions/agentic/memory/episodic.rb +20 -0
  46. data/lib/legion/extensions/agentic/memory/hologram/client.rb +23 -0
  47. data/lib/legion/extensions/agentic/memory/hologram/helpers/constants.rb +67 -0
  48. data/lib/legion/extensions/agentic/memory/hologram/helpers/hologram.rb +118 -0
  49. data/lib/legion/extensions/agentic/memory/hologram/helpers/hologram_engine.rb +117 -0
  50. data/lib/legion/extensions/agentic/memory/hologram/helpers/holographic_fragment.rb +70 -0
  51. data/lib/legion/extensions/agentic/memory/hologram/runners/cognitive_hologram.rb +110 -0
  52. data/lib/legion/extensions/agentic/memory/hologram/version.rb +13 -0
  53. data/lib/legion/extensions/agentic/memory/hologram.rb +22 -0
  54. data/lib/legion/extensions/agentic/memory/immune_memory/client.rb +19 -0
  55. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/constants.rb +84 -0
  56. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/encounter.rb +54 -0
  57. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/immune_memory_engine.rb +171 -0
  58. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/memory_cell.rb +97 -0
  59. data/lib/legion/extensions/agentic/memory/immune_memory/runners/cognitive_immune_memory.rb +76 -0
  60. data/lib/legion/extensions/agentic/memory/immune_memory/version.rb +13 -0
  61. data/lib/legion/extensions/agentic/memory/immune_memory.rb +20 -0
  62. data/lib/legion/extensions/agentic/memory/nostalgia/client.rb +31 -0
  63. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/constants.rb +68 -0
  64. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_engine.rb +148 -0
  65. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_event.rb +44 -0
  66. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/nostalgic_memory.rb +97 -0
  67. data/lib/legion/extensions/agentic/memory/nostalgia/runners/analysis.rb +61 -0
  68. data/lib/legion/extensions/agentic/memory/nostalgia/runners/recall.rb +60 -0
  69. data/lib/legion/extensions/agentic/memory/nostalgia/version.rb +13 -0
  70. data/lib/legion/extensions/agentic/memory/nostalgia.rb +21 -0
  71. data/lib/legion/extensions/agentic/memory/offloading/client.rb +29 -0
  72. data/lib/legion/extensions/agentic/memory/offloading/helpers/constants.rb +55 -0
  73. data/lib/legion/extensions/agentic/memory/offloading/helpers/external_store.rb +82 -0
  74. data/lib/legion/extensions/agentic/memory/offloading/helpers/offloaded_item.rb +65 -0
  75. data/lib/legion/extensions/agentic/memory/offloading/helpers/offloading_engine.rb +138 -0
  76. data/lib/legion/extensions/agentic/memory/offloading/runners/cognitive_offloading.rb +102 -0
  77. data/lib/legion/extensions/agentic/memory/offloading/version.rb +13 -0
  78. data/lib/legion/extensions/agentic/memory/offloading.rb +20 -0
  79. data/lib/legion/extensions/agentic/memory/paleontology/client.rb +19 -0
  80. data/lib/legion/extensions/agentic/memory/paleontology/helpers/constants.rb +69 -0
  81. data/lib/legion/extensions/agentic/memory/paleontology/helpers/excavation.rb +62 -0
  82. data/lib/legion/extensions/agentic/memory/paleontology/helpers/fossil.rb +125 -0
  83. data/lib/legion/extensions/agentic/memory/paleontology/helpers/paleontology_engine.rb +170 -0
  84. data/lib/legion/extensions/agentic/memory/paleontology/runners/cognitive_paleontology.rb +93 -0
  85. data/lib/legion/extensions/agentic/memory/paleontology/version.rb +13 -0
  86. data/lib/legion/extensions/agentic/memory/paleontology.rb +22 -0
  87. data/lib/legion/extensions/agentic/memory/palimpsest/client.rb +19 -0
  88. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/belief_layer.rb +81 -0
  89. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/constants.rb +56 -0
  90. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest.rb +124 -0
  91. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest_engine.rb +117 -0
  92. data/lib/legion/extensions/agentic/memory/palimpsest/runners/cognitive_palimpsest.rb +105 -0
  93. data/lib/legion/extensions/agentic/memory/palimpsest/version.rb +13 -0
  94. data/lib/legion/extensions/agentic/memory/palimpsest.rb +21 -0
  95. data/lib/legion/extensions/agentic/memory/reserve/client.rb +19 -0
  96. data/lib/legion/extensions/agentic/memory/reserve/helpers/constants.rb +58 -0
  97. data/lib/legion/extensions/agentic/memory/reserve/helpers/pathway.rb +112 -0
  98. data/lib/legion/extensions/agentic/memory/reserve/helpers/reserve_engine.rb +163 -0
  99. data/lib/legion/extensions/agentic/memory/reserve/runners/cognitive_reserve.rb +92 -0
  100. data/lib/legion/extensions/agentic/memory/reserve/version.rb +13 -0
  101. data/lib/legion/extensions/agentic/memory/reserve.rb +19 -0
  102. data/lib/legion/extensions/agentic/memory/semantic/actors/decay.rb +45 -0
  103. data/lib/legion/extensions/agentic/memory/semantic/client.rb +28 -0
  104. data/lib/legion/extensions/agentic/memory/semantic/helpers/concept.rb +101 -0
  105. data/lib/legion/extensions/agentic/memory/semantic/helpers/constants.rb +51 -0
  106. data/lib/legion/extensions/agentic/memory/semantic/helpers/knowledge_store.rb +145 -0
  107. data/lib/legion/extensions/agentic/memory/semantic/runners/semantic_memory.rb +94 -0
  108. data/lib/legion/extensions/agentic/memory/semantic/version.rb +13 -0
  109. data/lib/legion/extensions/agentic/memory/semantic.rb +20 -0
  110. data/lib/legion/extensions/agentic/memory/semantic_priming/client.rb +19 -0
  111. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/connection.rb +77 -0
  112. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/constants.rb +68 -0
  113. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/priming_network.rb +206 -0
  114. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/semantic_node.rb +81 -0
  115. data/lib/legion/extensions/agentic/memory/semantic_priming/runners/semantic_priming.rb +120 -0
  116. data/lib/legion/extensions/agentic/memory/semantic_priming/version.rb +13 -0
  117. data/lib/legion/extensions/agentic/memory/semantic_priming.rb +20 -0
  118. data/lib/legion/extensions/agentic/memory/semantic_satiation/client.rb +28 -0
  119. data/lib/legion/extensions/agentic/memory/semantic_satiation/helpers/concept.rb +82 -0
  120. data/lib/legion/extensions/agentic/memory/semantic_satiation/helpers/constants.rb +37 -0
  121. data/lib/legion/extensions/agentic/memory/semantic_satiation/helpers/satiation_engine.rb +103 -0
  122. data/lib/legion/extensions/agentic/memory/semantic_satiation/runners/semantic_satiation.rb +92 -0
  123. data/lib/legion/extensions/agentic/memory/semantic_satiation/version.rb +13 -0
  124. data/lib/legion/extensions/agentic/memory/semantic_satiation.rb +19 -0
  125. data/lib/legion/extensions/agentic/memory/source_monitoring/actors/decay.rb +31 -0
  126. data/lib/legion/extensions/agentic/memory/source_monitoring/client.rb +28 -0
  127. data/lib/legion/extensions/agentic/memory/source_monitoring/helpers/constants.rb +66 -0
  128. data/lib/legion/extensions/agentic/memory/source_monitoring/helpers/source_record.rb +94 -0
  129. data/lib/legion/extensions/agentic/memory/source_monitoring/helpers/source_tracker.rb +119 -0
  130. data/lib/legion/extensions/agentic/memory/source_monitoring/runners/source_monitoring.rb +95 -0
  131. data/lib/legion/extensions/agentic/memory/source_monitoring/version.rb +13 -0
  132. data/lib/legion/extensions/agentic/memory/source_monitoring.rb +19 -0
  133. data/lib/legion/extensions/agentic/memory/trace/actors/decay.rb +45 -0
  134. data/lib/legion/extensions/agentic/memory/trace/actors/tier_migration.rb +45 -0
  135. data/lib/legion/extensions/agentic/memory/trace/batch_decay.rb +44 -0
  136. data/lib/legion/extensions/agentic/memory/trace/client.rb +32 -0
  137. data/lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb +167 -0
  138. data/lib/legion/extensions/agentic/memory/trace/helpers/decay.rb +68 -0
  139. data/lib/legion/extensions/agentic/memory/trace/helpers/error_tracer.rb +94 -0
  140. data/lib/legion/extensions/agentic/memory/trace/helpers/store.rb +260 -0
  141. data/lib/legion/extensions/agentic/memory/trace/helpers/trace.rb +106 -0
  142. data/lib/legion/extensions/agentic/memory/trace/local_migrations/20260316000001_create_memory_traces.rb +31 -0
  143. data/lib/legion/extensions/agentic/memory/trace/local_migrations/20260316000002_create_memory_associations.rb +13 -0
  144. data/lib/legion/extensions/agentic/memory/trace/persistent_store.rb +94 -0
  145. data/lib/legion/extensions/agentic/memory/trace/quota.rb +55 -0
  146. data/lib/legion/extensions/agentic/memory/trace/runners/consolidation.rb +121 -0
  147. data/lib/legion/extensions/agentic/memory/trace/runners/traces.rb +105 -0
  148. data/lib/legion/extensions/agentic/memory/trace/version.rb +13 -0
  149. data/lib/legion/extensions/agentic/memory/trace.rb +54 -0
  150. data/lib/legion/extensions/agentic/memory/transfer/client.rb +28 -0
  151. data/lib/legion/extensions/agentic/memory/transfer/helpers/constants.rb +34 -0
  152. data/lib/legion/extensions/agentic/memory/transfer/helpers/domain_knowledge.rb +74 -0
  153. data/lib/legion/extensions/agentic/memory/transfer/helpers/transfer_engine.rb +188 -0
  154. data/lib/legion/extensions/agentic/memory/transfer/runners/transfer_learning.rb +77 -0
  155. data/lib/legion/extensions/agentic/memory/transfer/version.rb +13 -0
  156. data/lib/legion/extensions/agentic/memory/transfer.rb +19 -0
  157. data/lib/legion/extensions/agentic/memory/version.rb +11 -0
  158. data/lib/legion/extensions/agentic/memory.rb +35 -0
  159. data/spec/legion/extensions/agentic/memory/archaeology/client_spec.rb +38 -0
  160. data/spec/legion/extensions/agentic/memory/archaeology/cognitive_archaeology_spec.rb +7 -0
  161. data/spec/legion/extensions/agentic/memory/archaeology/helpers/archaeology_engine_spec.rb +165 -0
  162. data/spec/legion/extensions/agentic/memory/archaeology/helpers/artifact_spec.rb +196 -0
  163. data/spec/legion/extensions/agentic/memory/archaeology/helpers/constants_spec.rb +42 -0
  164. data/spec/legion/extensions/agentic/memory/archaeology/helpers/excavation_site_spec.rb +115 -0
  165. data/spec/legion/extensions/agentic/memory/archaeology/runners/cognitive_archaeology_spec.rb +96 -0
  166. data/spec/legion/extensions/agentic/memory/compression/helpers/compression_engine_spec.rb +138 -0
  167. data/spec/legion/extensions/agentic/memory/compression/helpers/constants_spec.rb +50 -0
  168. data/spec/legion/extensions/agentic/memory/compression/helpers/information_chunk_spec.rb +123 -0
  169. data/spec/legion/extensions/agentic/memory/compression/runners/cognitive_compression_spec.rb +80 -0
  170. data/spec/legion/extensions/agentic/memory/echo/client_spec.rb +20 -0
  171. data/spec/legion/extensions/agentic/memory/echo/cognitive_echo_spec.rb +7 -0
  172. data/spec/legion/extensions/agentic/memory/echo/helpers/echo_engine_spec.rb +152 -0
  173. data/spec/legion/extensions/agentic/memory/echo/helpers/echo_spec.rb +128 -0
  174. data/spec/legion/extensions/agentic/memory/echo/runners_spec.rb +80 -0
  175. data/spec/legion/extensions/agentic/memory/echo_chamber/client_spec.rb +84 -0
  176. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/chamber_engine_spec.rb +266 -0
  177. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/chamber_spec.rb +249 -0
  178. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/constants_spec.rb +130 -0
  179. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/echo_spec.rb +252 -0
  180. data/spec/legion/extensions/agentic/memory/echo_chamber/runners/cognitive_echo_chamber_spec.rb +185 -0
  181. data/spec/legion/extensions/agentic/memory/episodic/client_spec.rb +80 -0
  182. data/spec/legion/extensions/agentic/memory/episodic/episodic_buffer_spec.rb +15 -0
  183. data/spec/legion/extensions/agentic/memory/episodic/helpers/constants_spec.rb +86 -0
  184. data/spec/legion/extensions/agentic/memory/episodic/helpers/episode_spec.rb +188 -0
  185. data/spec/legion/extensions/agentic/memory/episodic/helpers/episodic_binding_spec.rb +114 -0
  186. data/spec/legion/extensions/agentic/memory/episodic/helpers/episodic_store_spec.rb +201 -0
  187. data/spec/legion/extensions/agentic/memory/episodic/runners/episodic_buffer_spec.rb +208 -0
  188. data/spec/legion/extensions/agentic/memory/hologram/client_spec.rb +89 -0
  189. data/spec/legion/extensions/agentic/memory/hologram/cognitive_hologram_spec.rb +39 -0
  190. data/spec/legion/extensions/agentic/memory/hologram/helpers/constants_spec.rb +143 -0
  191. data/spec/legion/extensions/agentic/memory/hologram/helpers/hologram_engine_spec.rb +268 -0
  192. data/spec/legion/extensions/agentic/memory/hologram/helpers/hologram_spec.rb +256 -0
  193. data/spec/legion/extensions/agentic/memory/hologram/helpers/holographic_fragment_spec.rb +213 -0
  194. data/spec/legion/extensions/agentic/memory/hologram/runners/cognitive_hologram_spec.rb +239 -0
  195. data/spec/legion/extensions/agentic/memory/immune_memory/client_spec.rb +18 -0
  196. data/spec/legion/extensions/agentic/memory/immune_memory/cognitive_immune_memory_spec.rb +7 -0
  197. data/spec/legion/extensions/agentic/memory/immune_memory/helpers/encounter_spec.rb +85 -0
  198. data/spec/legion/extensions/agentic/memory/immune_memory/helpers/immune_memory_engine_spec.rb +203 -0
  199. data/spec/legion/extensions/agentic/memory/immune_memory/helpers/memory_cell_spec.rb +190 -0
  200. data/spec/legion/extensions/agentic/memory/immune_memory/runners_spec.rb +83 -0
  201. data/spec/legion/extensions/agentic/memory/nostalgia/client_spec.rb +71 -0
  202. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/constants_spec.rb +65 -0
  203. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_engine_spec.rb +191 -0
  204. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_event_spec.rb +59 -0
  205. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/nostalgic_memory_spec.rb +134 -0
  206. data/spec/legion/extensions/agentic/memory/nostalgia/runners/analysis_spec.rb +97 -0
  207. data/spec/legion/extensions/agentic/memory/nostalgia/runners/recall_spec.rb +81 -0
  208. data/spec/legion/extensions/agentic/memory/offloading/client_spec.rb +30 -0
  209. data/spec/legion/extensions/agentic/memory/offloading/helpers/constants_spec.rb +71 -0
  210. data/spec/legion/extensions/agentic/memory/offloading/helpers/external_store_spec.rb +158 -0
  211. data/spec/legion/extensions/agentic/memory/offloading/helpers/offloaded_item_spec.rb +123 -0
  212. data/spec/legion/extensions/agentic/memory/offloading/helpers/offloading_engine_spec.rb +250 -0
  213. data/spec/legion/extensions/agentic/memory/offloading/runners/cognitive_offloading_spec.rb +194 -0
  214. data/spec/legion/extensions/agentic/memory/paleontology/client_spec.rb +27 -0
  215. data/spec/legion/extensions/agentic/memory/paleontology/cognitive_paleontology_spec.rb +7 -0
  216. data/spec/legion/extensions/agentic/memory/paleontology/helpers/constants_spec.rb +33 -0
  217. data/spec/legion/extensions/agentic/memory/paleontology/helpers/excavation_spec.rb +71 -0
  218. data/spec/legion/extensions/agentic/memory/paleontology/helpers/fossil_spec.rb +134 -0
  219. data/spec/legion/extensions/agentic/memory/paleontology/helpers/paleontology_engine_spec.rb +148 -0
  220. data/spec/legion/extensions/agentic/memory/paleontology/runners/cognitive_paleontology_spec.rb +89 -0
  221. data/spec/legion/extensions/agentic/memory/palimpsest/client_spec.rb +58 -0
  222. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/belief_layer_spec.rb +150 -0
  223. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/constants_spec.rb +63 -0
  224. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest_engine_spec.rb +164 -0
  225. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest_spec.rb +172 -0
  226. data/spec/legion/extensions/agentic/memory/palimpsest/runners/cognitive_palimpsest_spec.rb +165 -0
  227. data/spec/legion/extensions/agentic/memory/reserve/client_spec.rb +37 -0
  228. data/spec/legion/extensions/agentic/memory/reserve/helpers/pathway_spec.rb +165 -0
  229. data/spec/legion/extensions/agentic/memory/reserve/helpers/reserve_engine_spec.rb +196 -0
  230. data/spec/legion/extensions/agentic/memory/reserve/runners/cognitive_reserve_spec.rb +96 -0
  231. data/spec/legion/extensions/agentic/memory/semantic/client_spec.rb +35 -0
  232. data/spec/legion/extensions/agentic/memory/semantic/helpers/concept_spec.rb +119 -0
  233. data/spec/legion/extensions/agentic/memory/semantic/helpers/knowledge_store_spec.rb +140 -0
  234. data/spec/legion/extensions/agentic/memory/semantic/runners/semantic_memory_spec.rb +103 -0
  235. data/spec/legion/extensions/agentic/memory/semantic_priming/helpers/connection_spec.rb +98 -0
  236. data/spec/legion/extensions/agentic/memory/semantic_priming/helpers/priming_network_spec.rb +208 -0
  237. data/spec/legion/extensions/agentic/memory/semantic_priming/helpers/semantic_node_spec.rb +125 -0
  238. data/spec/legion/extensions/agentic/memory/semantic_priming/semantic_priming_spec.rb +7 -0
  239. data/spec/legion/extensions/agentic/memory/semantic_satiation/client_spec.rb +19 -0
  240. data/spec/legion/extensions/agentic/memory/semantic_satiation/helpers/concept_spec.rb +167 -0
  241. data/spec/legion/extensions/agentic/memory/semantic_satiation/helpers/constants_spec.rb +81 -0
  242. data/spec/legion/extensions/agentic/memory/semantic_satiation/helpers/satiation_engine_spec.rb +189 -0
  243. data/spec/legion/extensions/agentic/memory/semantic_satiation/runners/semantic_satiation_spec.rb +176 -0
  244. data/spec/legion/extensions/agentic/memory/source_monitoring/client_spec.rb +25 -0
  245. data/spec/legion/extensions/agentic/memory/source_monitoring/helpers/source_record_spec.rb +152 -0
  246. data/spec/legion/extensions/agentic/memory/source_monitoring/helpers/source_tracker_spec.rb +139 -0
  247. data/spec/legion/extensions/agentic/memory/source_monitoring/runners/source_monitoring_spec.rb +102 -0
  248. data/spec/legion/extensions/agentic/memory/trace/actors/decay_spec.rb +62 -0
  249. data/spec/legion/extensions/agentic/memory/trace/actors/tier_migration_spec.rb +62 -0
  250. data/spec/legion/extensions/agentic/memory/trace/batch_decay_spec.rb +26 -0
  251. data/spec/legion/extensions/agentic/memory/trace/client_spec.rb +62 -0
  252. data/spec/legion/extensions/agentic/memory/trace/helpers/decay_spec.rb +134 -0
  253. data/spec/legion/extensions/agentic/memory/trace/helpers/store_spec.rb +217 -0
  254. data/spec/legion/extensions/agentic/memory/trace/helpers/trace_spec.rb +120 -0
  255. data/spec/legion/extensions/agentic/memory/trace/local_persistence_spec.rb +255 -0
  256. data/spec/legion/extensions/agentic/memory/trace/memory_spec.rb +11 -0
  257. data/spec/legion/extensions/agentic/memory/trace/persistent_store_spec.rb +50 -0
  258. data/spec/legion/extensions/agentic/memory/trace/quota_spec.rb +56 -0
  259. data/spec/legion/extensions/agentic/memory/trace/runners/consolidation_spec.rb +118 -0
  260. data/spec/legion/extensions/agentic/memory/trace/runners/traces_spec.rb +69 -0
  261. data/spec/legion/extensions/agentic/memory/transfer/client_spec.rb +18 -0
  262. data/spec/legion/extensions/agentic/memory/transfer/helpers/constants_spec.rb +41 -0
  263. data/spec/legion/extensions/agentic/memory/transfer/helpers/domain_knowledge_spec.rb +134 -0
  264. data/spec/legion/extensions/agentic/memory/transfer/helpers/transfer_engine_spec.rb +299 -0
  265. data/spec/legion/extensions/agentic/memory/transfer/runners/transfer_learning_spec.rb +144 -0
  266. data/spec/spec_helper.rb +46 -0
  267. metadata +351 -0
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module SemanticPriming
8
+ module Helpers
9
+ class PrimingNetwork
10
+ include Constants
11
+
12
+ def initialize
13
+ @nodes = {}
14
+ @connections = {}
15
+ @adjacency = Hash.new { |h, k| h[k] = [] }
16
+ end
17
+
18
+ def add_node(label:, node_type: :concept)
19
+ prune_nodes_if_needed
20
+ node = SemanticNode.new(label: label, node_type: node_type)
21
+ @nodes[node.id] = node
22
+ node
23
+ end
24
+
25
+ def remove_node(node_id:)
26
+ node = @nodes.delete(node_id)
27
+ return nil unless node
28
+
29
+ @adjacency.delete(node_id)
30
+ @adjacency.each_value { |list| list.reject! { |cid| connection_involves?(cid, node_id) } }
31
+ @connections.reject! { |_, c| c.source_id == node_id || c.target_id == node_id }
32
+ node
33
+ end
34
+
35
+ def connect(source_id:, target_id:, weight: DEFAULT_WEIGHT)
36
+ return nil unless @nodes[source_id] && @nodes[target_id]
37
+ return nil if source_id == target_id
38
+
39
+ prune_connections_if_needed
40
+ conn = Connection.new(source_id: source_id, target_id: target_id, weight: weight)
41
+ @connections[conn.id] = conn
42
+ @adjacency[source_id] << conn.id
43
+ @adjacency[target_id] << conn.id
44
+ conn
45
+ end
46
+
47
+ def prime_node(node_id:, amount: PRIMING_BOOST)
48
+ node = @nodes[node_id]
49
+ return nil unless node
50
+
51
+ node.prime!(amount: amount)
52
+ node
53
+ end
54
+
55
+ def spread_activation(source_id:, depth: MAX_SPREAD_DEPTH)
56
+ source = @nodes[source_id]
57
+ return nil unless source
58
+
59
+ activated = {}
60
+ spread_recursive(source_id, source.activation, depth, 0, activated)
61
+ activated.map { |nid, amount| { node_id: nid, label: @nodes[nid]&.label, activation_added: amount } }
62
+ end
63
+
64
+ def prime_and_spread(node_id:, amount: PRIMING_BOOST, depth: MAX_SPREAD_DEPTH)
65
+ node = prime_node(node_id: node_id, amount: amount)
66
+ return nil unless node
67
+
68
+ node.access!
69
+ spread = spread_activation(source_id: node_id, depth: depth)
70
+ { primed_node: node.to_h, spread: spread }
71
+ end
72
+
73
+ def decay_all!
74
+ @nodes.each_value(&:decay!)
75
+ @connections.each_value { |c| c.weaken!(amount: WEIGHT_DECAY_RATE) }
76
+ prune_weak_connections
77
+ { nodes_decayed: @nodes.size, connections_remaining: @connections.size }
78
+ end
79
+
80
+ def reset_all! = @nodes.each_value(&:reset!) && { nodes_reset: @nodes.size }
81
+ def find_node_by_label(label:) = @nodes.values.find { |n| n.label == label.to_s }
82
+
83
+ def neighbors(node_id:)
84
+ conn_ids = @adjacency[node_id] || []
85
+ conn_ids.filter_map do |cid|
86
+ conn = @connections[cid]
87
+ next unless conn
88
+
89
+ other_id = conn.source_id == node_id ? conn.target_id : conn.source_id
90
+ @nodes[other_id]
91
+ end
92
+ end
93
+
94
+ def connection_between(source_id:, target_id:)
95
+ @connections.values.find do |c|
96
+ (c.source_id == source_id && c.target_id == target_id) ||
97
+ (c.source_id == target_id && c.target_id == source_id)
98
+ end
99
+ end
100
+
101
+ def primed_nodes = @nodes.values.select(&:primed?)
102
+ def active_nodes = @nodes.values.select(&:active?)
103
+ def most_primed(limit: 5) = @nodes.values.sort_by { |n| -n.activation }.first(limit)
104
+ def strongest_connections(limit: 5) = @connections.values.sort_by { |c| -c.weight }.first(limit)
105
+
106
+ def average_activation
107
+ return DEFAULT_ACTIVATION if @nodes.empty?
108
+
109
+ vals = @nodes.values.map(&:activation)
110
+ (vals.sum / vals.size).round(10)
111
+ end
112
+
113
+ def average_connection_weight
114
+ return DEFAULT_WEIGHT if @connections.empty?
115
+
116
+ vals = @connections.values.map(&:weight)
117
+ (vals.sum / vals.size).round(10)
118
+ end
119
+
120
+ def network_density
121
+ return 0.0 if @nodes.size < 2
122
+
123
+ (@connections.size.to_f / (@nodes.size * (@nodes.size - 1) / 2)).round(10)
124
+ end
125
+
126
+ def priming_report
127
+ to_h.merge(
128
+ average_weight: average_connection_weight,
129
+ most_primed: most_primed(limit: 3).map(&:to_h),
130
+ strongest_connections: strongest_connections(limit: 3).map(&:to_h)
131
+ )
132
+ end
133
+
134
+ def to_h
135
+ {
136
+ total_nodes: @nodes.size,
137
+ total_connections: @connections.size,
138
+ primed_count: primed_nodes.size,
139
+ active_count: active_nodes.size,
140
+ average_activation: average_activation,
141
+ network_density: network_density
142
+ }
143
+ end
144
+
145
+ private
146
+
147
+ def spread_recursive(node_id, activation, max_depth, current_depth, activated)
148
+ return if current_depth >= max_depth || activation < ACTIVATION_THRESHOLD
149
+
150
+ each_neighbor(node_id) do |conn, other_id, target_node|
151
+ next if activated.key?(other_id)
152
+
153
+ amount = (conn.spreading_amount(activation) * (DEPTH_DECAY_FACTOR**current_depth)).round(10)
154
+ next if amount < ACTIVATION_THRESHOLD
155
+
156
+ conn.traverse!
157
+ target_node.prime!(amount: amount)
158
+ activated[other_id] = amount
159
+ spread_recursive(other_id, amount, max_depth, current_depth + 1, activated)
160
+ end
161
+ end
162
+
163
+ def each_neighbor(node_id)
164
+ (@adjacency[node_id] || []).each do |cid|
165
+ conn = @connections[cid]
166
+ next unless conn
167
+
168
+ other_id = conn.source_id == node_id ? conn.target_id : conn.source_id
169
+ target_node = @nodes[other_id]
170
+ yield conn, other_id, target_node if target_node
171
+ end
172
+ end
173
+
174
+ def connection_involves?(cid, nid)
175
+ (c = @connections[cid]) && (c.source_id == nid || c.target_id == nid)
176
+ end
177
+
178
+ def prune_nodes_if_needed
179
+ return if @nodes.size < MAX_NODES
180
+
181
+ remove_node(node_id: @nodes.values.min_by(&:activation).id)
182
+ end
183
+
184
+ def prune_connections_if_needed
185
+ return if @connections.size < MAX_CONNECTIONS
186
+
187
+ remove_connection(@connections.values.min_by(&:weight).id)
188
+ end
189
+
190
+ def prune_weak_connections
191
+ @connections.each { |id, c| remove_connection(id) if c.weight <= MIN_WEIGHT }
192
+ end
193
+
194
+ def remove_connection(conn_id)
195
+ return unless (conn = @connections.delete(conn_id))
196
+
197
+ @adjacency[conn.source_id]&.delete(conn_id)
198
+ @adjacency[conn.target_id]&.delete(conn_id)
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Memory
9
+ module SemanticPriming
10
+ module Helpers
11
+ class SemanticNode
12
+ include Constants
13
+
14
+ attr_reader :id, :label, :node_type, :activation, :prime_count,
15
+ :access_count, :created_at
16
+
17
+ def initialize(label:, node_type: :concept, activation: DEFAULT_ACTIVATION)
18
+ @id = SecureRandom.uuid
19
+ @label = label.to_s
20
+ @node_type = node_type.to_sym
21
+ @activation = activation.to_f.clamp(0.0, MAX_ACTIVATION).round(10)
22
+ @prime_count = 0
23
+ @access_count = 0
24
+ @created_at = Time.now.utc
25
+ end
26
+
27
+ def prime!(amount: PRIMING_BOOST)
28
+ @activation = (@activation + amount).clamp(0.0, MAX_ACTIVATION).round(10)
29
+ @prime_count += 1
30
+ self
31
+ end
32
+
33
+ def decay!
34
+ @activation = (@activation - ACTIVATION_DECAY).clamp(0.0, MAX_ACTIVATION).round(10)
35
+ self
36
+ end
37
+
38
+ def access!
39
+ @access_count += 1
40
+ self
41
+ end
42
+
43
+ def reset!
44
+ @activation = RESTING_ACTIVATION
45
+ self
46
+ end
47
+
48
+ def primed?
49
+ @activation >= 0.4
50
+ end
51
+
52
+ def active?
53
+ @activation > ACTIVATION_THRESHOLD
54
+ end
55
+
56
+ def activation_label
57
+ match = ACTIVATION_LABELS.find { |range, _| range.cover?(@activation) }
58
+ match ? match.last : :unprimed
59
+ end
60
+
61
+ def to_h
62
+ {
63
+ id: @id,
64
+ label: @label,
65
+ node_type: @node_type,
66
+ activation: @activation,
67
+ activation_label: activation_label,
68
+ primed: primed?,
69
+ active: active?,
70
+ prime_count: @prime_count,
71
+ access_count: @access_count,
72
+ created_at: @created_at
73
+ }
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module SemanticPriming
8
+ module Runners
9
+ module SemanticPriming
10
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
11
+
12
+ def add_node(label:, node_type: :concept, engine: nil, **)
13
+ eng = engine || default_engine
14
+ node = eng.add_node(label: label, node_type: node_type)
15
+ { success: true, node: node.to_h }
16
+ end
17
+
18
+ def remove_node(node_id:, engine: nil, **)
19
+ eng = engine || default_engine
20
+ node = eng.remove_node(node_id: node_id)
21
+ return { success: false, error: 'node not found' } unless node
22
+
23
+ { success: true, removed: node.to_h }
24
+ end
25
+
26
+ def connect_nodes(source_id:, target_id:, weight: nil, engine: nil, **)
27
+ eng = engine || default_engine
28
+ w = weight || Helpers::Constants::DEFAULT_WEIGHT
29
+ conn = eng.connect(source_id: source_id, target_id: target_id, weight: w)
30
+ return { success: false, error: 'invalid nodes or self-connection' } unless conn
31
+
32
+ { success: true, connection: conn.to_h }
33
+ end
34
+
35
+ def prime(node_id:, amount: nil, engine: nil, **)
36
+ eng = engine || default_engine
37
+ amt = amount || Helpers::Constants::PRIMING_BOOST
38
+ node = eng.prime_node(node_id: node_id, amount: amt)
39
+ return { success: false, error: 'node not found' } unless node
40
+
41
+ { success: true, node: node.to_h }
42
+ end
43
+
44
+ def prime_and_spread(node_id:, amount: nil, depth: nil, engine: nil, **)
45
+ eng = engine || default_engine
46
+ amt = amount || Helpers::Constants::PRIMING_BOOST
47
+ d = depth || Helpers::Constants::MAX_SPREAD_DEPTH
48
+ result = eng.prime_and_spread(node_id: node_id, amount: amt, depth: d)
49
+ return { success: false, error: 'node not found' } unless result
50
+
51
+ { success: true, **result }
52
+ end
53
+
54
+ def spread_activation(source_id:, depth: nil, engine: nil, **)
55
+ eng = engine || default_engine
56
+ d = depth || Helpers::Constants::MAX_SPREAD_DEPTH
57
+ result = eng.spread_activation(source_id: source_id, depth: d)
58
+ return { success: false, error: 'node not found' } unless result
59
+
60
+ { success: true, activated: result }
61
+ end
62
+
63
+ def decay(engine: nil, **)
64
+ eng = engine || default_engine
65
+ result = eng.decay_all!
66
+ { success: true, **result }
67
+ end
68
+
69
+ def reset(engine: nil, **)
70
+ eng = engine || default_engine
71
+ result = eng.reset_all!
72
+ { success: true, **result }
73
+ end
74
+
75
+ def find_node(label:, engine: nil, **)
76
+ eng = engine || default_engine
77
+ node = eng.find_node_by_label(label: label)
78
+ return { success: false, error: 'node not found' } unless node
79
+
80
+ { success: true, node: node.to_h }
81
+ end
82
+
83
+ def neighbors(node_id:, engine: nil, **)
84
+ eng = engine || default_engine
85
+ nodes = eng.neighbors(node_id: node_id)
86
+ { success: true, neighbors: nodes.map(&:to_h) }
87
+ end
88
+
89
+ def primed_nodes(engine: nil, **)
90
+ eng = engine || default_engine
91
+ { success: true, nodes: eng.primed_nodes.map(&:to_h) }
92
+ end
93
+
94
+ def most_primed(limit: 5, engine: nil, **)
95
+ eng = engine || default_engine
96
+ { success: true, nodes: eng.most_primed(limit: limit).map(&:to_h) }
97
+ end
98
+
99
+ def priming_report(engine: nil, **)
100
+ eng = engine || default_engine
101
+ { success: true, report: eng.priming_report }
102
+ end
103
+
104
+ def status(engine: nil, **)
105
+ eng = engine || default_engine
106
+ { success: true, **eng.to_h }
107
+ end
108
+
109
+ private
110
+
111
+ def default_engine
112
+ @default_engine ||= Helpers::PrimingNetwork.new
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module SemanticPriming
8
+ VERSION = '0.1.0'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'semantic_priming/version'
4
+ require_relative 'semantic_priming/helpers/constants'
5
+ require_relative 'semantic_priming/helpers/semantic_node'
6
+ require_relative 'semantic_priming/helpers/connection'
7
+ require_relative 'semantic_priming/helpers/priming_network'
8
+ require_relative 'semantic_priming/runners/semantic_priming'
9
+ require_relative 'semantic_priming/client'
10
+
11
+ module Legion
12
+ module Extensions
13
+ module Agentic
14
+ module Memory
15
+ module SemanticPriming
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/memory/semantic_satiation/helpers/constants'
4
+ require 'legion/extensions/agentic/memory/semantic_satiation/helpers/concept'
5
+ require 'legion/extensions/agentic/memory/semantic_satiation/helpers/satiation_engine'
6
+ require 'legion/extensions/agentic/memory/semantic_satiation/runners/semantic_satiation'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Agentic
11
+ module Memory
12
+ module SemanticSatiation
13
+ class Client
14
+ include Runners::SemanticSatiation
15
+
16
+ def initialize(**)
17
+ @satiation_engine = Helpers::SatiationEngine.new
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :satiation_engine
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Memory
9
+ module SemanticSatiation
10
+ module Helpers
11
+ class Concept
12
+ include Constants
13
+
14
+ attr_reader :id, :label, :domain, :fluency, :exposure_count,
15
+ :last_exposed_at, :created_at
16
+
17
+ def initialize(label:, domain: :general)
18
+ @id = SecureRandom.uuid
19
+ @label = label
20
+ @domain = domain
21
+ @fluency = DEFAULT_FLUENCY
22
+ @exposure_count = 0
23
+ @last_exposed_at = nil
24
+ @created_at = Time.now.utc
25
+ end
26
+
27
+ def expose!
28
+ @exposure_count += 1
29
+ @fluency = (@fluency - SATIATION_RATE).clamp(0.0, DEFAULT_FLUENCY).round(10)
30
+ @last_exposed_at = Time.now.utc
31
+ end
32
+
33
+ def recover!(amount: RECOVERY_RATE)
34
+ @fluency = (@fluency + amount).clamp(0.0, DEFAULT_FLUENCY).round(10)
35
+ end
36
+
37
+ def satiated?
38
+ fluency < (DEFAULT_FLUENCY - SATIATION_THRESHOLD)
39
+ end
40
+
41
+ def fluency_label
42
+ FLUENCY_LABELS.find { |range, _| range.cover?(fluency) }&.last || :meaningless
43
+ end
44
+
45
+ def novelty
46
+ saturation = [exposure_count.to_f / 50.0, 1.0].min
47
+ (DEFAULT_FLUENCY - saturation).clamp(0.0, DEFAULT_FLUENCY).round(10)
48
+ end
49
+
50
+ def novelty_label
51
+ n = novelty
52
+ NOVELTY_LABELS.find { |range, _| range.cover?(n) }&.last || :saturated
53
+ end
54
+
55
+ def time_since_exposure
56
+ return nil unless last_exposed_at
57
+
58
+ Time.now.utc - last_exposed_at
59
+ end
60
+
61
+ def to_h
62
+ {
63
+ id: id,
64
+ label: label,
65
+ domain: domain,
66
+ fluency: fluency,
67
+ fluency_label: fluency_label,
68
+ novelty: novelty,
69
+ novelty_label: novelty_label,
70
+ exposure_count: exposure_count,
71
+ satiated: satiated?,
72
+ last_exposed_at: last_exposed_at,
73
+ created_at: created_at
74
+ }
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module SemanticSatiation
8
+ module Helpers
9
+ module Constants
10
+ MAX_CONCEPTS = 300
11
+ SATIATION_RATE = 0.08
12
+ RECOVERY_RATE = 0.03
13
+ SATIATION_THRESHOLD = 0.7
14
+ DEFAULT_FLUENCY = 1.0
15
+
16
+ FLUENCY_LABELS = {
17
+ (0.8..) => :fluent,
18
+ (0.6...0.8) => :normal,
19
+ (0.4...0.6) => :reduced,
20
+ (0.2...0.4) => :satiated,
21
+ (..0.2) => :meaningless
22
+ }.freeze
23
+
24
+ NOVELTY_LABELS = {
25
+ (0.8..) => :novel,
26
+ (0.6...0.8) => :familiar,
27
+ (0.4...0.6) => :routine,
28
+ (0.2...0.4) => :overexposed,
29
+ (..0.2) => :saturated
30
+ }.freeze
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module SemanticSatiation
8
+ module Helpers
9
+ class SatiationEngine
10
+ include Constants
11
+
12
+ attr_reader :concepts
13
+
14
+ def initialize
15
+ @concepts = {}
16
+ end
17
+
18
+ def register_concept(label:, domain: :general)
19
+ prune_saturated if @concepts.size >= MAX_CONCEPTS
20
+
21
+ existing = find_by_label(label)
22
+ return existing if existing
23
+
24
+ concept = Concept.new(label: label, domain: domain)
25
+ @concepts[concept.id] = concept
26
+ concept
27
+ end
28
+
29
+ def expose_concept(concept_id:)
30
+ concept = @concepts[concept_id]
31
+ return { error: :not_found, concept_id: concept_id } unless concept
32
+
33
+ concept.expose!
34
+ concept.to_h
35
+ end
36
+
37
+ def expose_by_label(label:, domain: :general)
38
+ concept = find_by_label(label) || register_concept(label: label, domain: domain)
39
+ concept.expose!
40
+ concept.to_h
41
+ end
42
+
43
+ def recover_all
44
+ @concepts.each_value(&:recover!)
45
+ { recovered: @concepts.size }
46
+ end
47
+
48
+ def satiated_concepts
49
+ @concepts.values.select(&:satiated?)
50
+ end
51
+
52
+ def most_exposed(limit: 5)
53
+ @concepts.values.sort_by { |c| -c.exposure_count }.first(limit)
54
+ end
55
+
56
+ def freshest(limit: 5)
57
+ @concepts.values.sort_by { |c| -c.fluency }.first(limit)
58
+ end
59
+
60
+ def domain_satiation(domain:)
61
+ domain_concepts = @concepts.values.select { |c| c.domain == domain }
62
+ return 0.0 if domain_concepts.empty?
63
+
64
+ avg = domain_concepts.sum(&:fluency) / domain_concepts.size.to_f
65
+ avg.round(10)
66
+ end
67
+
68
+ def novelty_report
69
+ distribution = Hash.new(0)
70
+ @concepts.each_value do |c|
71
+ distribution[c.novelty_label] += 1
72
+ end
73
+ distribution
74
+ end
75
+
76
+ def prune_saturated
77
+ to_remove = @concepts.select { |_, c| c.fluency <= 0.05 }.keys
78
+ to_remove.each { |id| @concepts.delete(id) }
79
+ to_remove.size
80
+ end
81
+
82
+ def to_h
83
+ {
84
+ concept_count: @concepts.size,
85
+ satiated_count: satiated_concepts.size,
86
+ novelty_report: novelty_report,
87
+ most_exposed: most_exposed.map(&:to_h),
88
+ freshest: freshest.map(&:to_h)
89
+ }
90
+ end
91
+
92
+ private
93
+
94
+ def find_by_label(label)
95
+ @concepts.values.find { |c| c.label == label }
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end