lex-agentic-language 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 (126) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +13 -0
  6. data/lex-agentic-language.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/language/conceptual_blending/client.rb +25 -0
  8. data/lib/legion/extensions/agentic/language/conceptual_blending/helpers/blend.rb +91 -0
  9. data/lib/legion/extensions/agentic/language/conceptual_blending/helpers/blending_engine.rb +171 -0
  10. data/lib/legion/extensions/agentic/language/conceptual_blending/helpers/constants.rb +35 -0
  11. data/lib/legion/extensions/agentic/language/conceptual_blending/helpers/mental_space.rb +51 -0
  12. data/lib/legion/extensions/agentic/language/conceptual_blending/runners/conceptual_blending.rb +106 -0
  13. data/lib/legion/extensions/agentic/language/conceptual_blending/version.rb +13 -0
  14. data/lib/legion/extensions/agentic/language/conceptual_blending.rb +20 -0
  15. data/lib/legion/extensions/agentic/language/conceptual_metaphor/client.rb +19 -0
  16. data/lib/legion/extensions/agentic/language/conceptual_metaphor/helpers/constants.rb +49 -0
  17. data/lib/legion/extensions/agentic/language/conceptual_metaphor/helpers/metaphor.rb +109 -0
  18. data/lib/legion/extensions/agentic/language/conceptual_metaphor/helpers/metaphor_engine.rb +154 -0
  19. data/lib/legion/extensions/agentic/language/conceptual_metaphor/runners/conceptual_metaphor.rb +107 -0
  20. data/lib/legion/extensions/agentic/language/conceptual_metaphor/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/language/conceptual_metaphor.rb +19 -0
  22. data/lib/legion/extensions/agentic/language/frame_semantics/helpers/client.rb +23 -0
  23. data/lib/legion/extensions/agentic/language/frame_semantics/helpers/constants.rb +32 -0
  24. data/lib/legion/extensions/agentic/language/frame_semantics/helpers/frame.rb +109 -0
  25. data/lib/legion/extensions/agentic/language/frame_semantics/helpers/frame_engine.rb +139 -0
  26. data/lib/legion/extensions/agentic/language/frame_semantics/helpers/frame_instance.rb +51 -0
  27. data/lib/legion/extensions/agentic/language/frame_semantics/runners/frame_semantics.rb +108 -0
  28. data/lib/legion/extensions/agentic/language/frame_semantics/version.rb +13 -0
  29. data/lib/legion/extensions/agentic/language/frame_semantics.rb +20 -0
  30. data/lib/legion/extensions/agentic/language/grammar/client.rb +29 -0
  31. data/lib/legion/extensions/agentic/language/grammar/helpers/constants.rb +38 -0
  32. data/lib/legion/extensions/agentic/language/grammar/helpers/construal.rb +68 -0
  33. data/lib/legion/extensions/agentic/language/grammar/helpers/construction.rb +68 -0
  34. data/lib/legion/extensions/agentic/language/grammar/helpers/grammar_engine.rb +119 -0
  35. data/lib/legion/extensions/agentic/language/grammar/runners/cognitive_grammar.rb +100 -0
  36. data/lib/legion/extensions/agentic/language/grammar/version.rb +13 -0
  37. data/lib/legion/extensions/agentic/language/grammar.rb +20 -0
  38. data/lib/legion/extensions/agentic/language/inner_speech/client.rb +19 -0
  39. data/lib/legion/extensions/agentic/language/inner_speech/helpers/constants.rb +53 -0
  40. data/lib/legion/extensions/agentic/language/inner_speech/helpers/inner_voice.rb +141 -0
  41. data/lib/legion/extensions/agentic/language/inner_speech/helpers/speech_stream.rb +128 -0
  42. data/lib/legion/extensions/agentic/language/inner_speech/helpers/utterance.rb +90 -0
  43. data/lib/legion/extensions/agentic/language/inner_speech/runners/inner_speech.rb +87 -0
  44. data/lib/legion/extensions/agentic/language/inner_speech/version.rb +13 -0
  45. data/lib/legion/extensions/agentic/language/inner_speech.rb +20 -0
  46. data/lib/legion/extensions/agentic/language/language/client.rb +26 -0
  47. data/lib/legion/extensions/agentic/language/language/helpers/constants.rb +43 -0
  48. data/lib/legion/extensions/agentic/language/language/helpers/lexicon.rb +63 -0
  49. data/lib/legion/extensions/agentic/language/language/helpers/summarizer.rb +167 -0
  50. data/lib/legion/extensions/agentic/language/language/runners/language.rb +134 -0
  51. data/lib/legion/extensions/agentic/language/language/version.rb +13 -0
  52. data/lib/legion/extensions/agentic/language/language.rb +19 -0
  53. data/lib/legion/extensions/agentic/language/narrative_reasoning/client.rb +28 -0
  54. data/lib/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative.rb +123 -0
  55. data/lib/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_engine.rb +122 -0
  56. data/lib/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_event.rb +41 -0
  57. data/lib/legion/extensions/agentic/language/narrative_reasoning/runners/narrative_reasoning.rb +122 -0
  58. data/lib/legion/extensions/agentic/language/narrative_reasoning/version.rb +13 -0
  59. data/lib/legion/extensions/agentic/language/narrative_reasoning.rb +18 -0
  60. data/lib/legion/extensions/agentic/language/narrator/client.rb +27 -0
  61. data/lib/legion/extensions/agentic/language/narrator/helpers/constants.rb +69 -0
  62. data/lib/legion/extensions/agentic/language/narrator/helpers/journal.rb +68 -0
  63. data/lib/legion/extensions/agentic/language/narrator/helpers/llm_enhancer.rb +105 -0
  64. data/lib/legion/extensions/agentic/language/narrator/helpers/prose.rb +122 -0
  65. data/lib/legion/extensions/agentic/language/narrator/helpers/synthesizer.rb +138 -0
  66. data/lib/legion/extensions/agentic/language/narrator/runners/narrator.rb +196 -0
  67. data/lib/legion/extensions/agentic/language/narrator/version.rb +13 -0
  68. data/lib/legion/extensions/agentic/language/narrator.rb +21 -0
  69. data/lib/legion/extensions/agentic/language/pragmatic_inference/client.rb +28 -0
  70. data/lib/legion/extensions/agentic/language/pragmatic_inference/helpers/constants.rb +52 -0
  71. data/lib/legion/extensions/agentic/language/pragmatic_inference/helpers/pragmatic_engine.rb +164 -0
  72. data/lib/legion/extensions/agentic/language/pragmatic_inference/helpers/utterance.rb +84 -0
  73. data/lib/legion/extensions/agentic/language/pragmatic_inference/runners/pragmatic_inference.rb +136 -0
  74. data/lib/legion/extensions/agentic/language/pragmatic_inference/version.rb +13 -0
  75. data/lib/legion/extensions/agentic/language/pragmatic_inference.rb +18 -0
  76. data/lib/legion/extensions/agentic/language/version.rb +11 -0
  77. data/lib/legion/extensions/agentic/language.rb +28 -0
  78. data/spec/legion/extensions/agentic/language/conceptual_blending/client_spec.rb +78 -0
  79. data/spec/legion/extensions/agentic/language/conceptual_blending/helpers/blend_spec.rb +141 -0
  80. data/spec/legion/extensions/agentic/language/conceptual_blending/helpers/blending_engine_spec.rb +211 -0
  81. data/spec/legion/extensions/agentic/language/conceptual_blending/helpers/mental_space_spec.rb +85 -0
  82. data/spec/legion/extensions/agentic/language/conceptual_blending/runners/conceptual_blending_spec.rb +162 -0
  83. data/spec/legion/extensions/agentic/language/conceptual_metaphor/client_spec.rb +29 -0
  84. data/spec/legion/extensions/agentic/language/conceptual_metaphor/helpers/metaphor_engine_spec.rb +166 -0
  85. data/spec/legion/extensions/agentic/language/conceptual_metaphor/helpers/metaphor_spec.rb +133 -0
  86. data/spec/legion/extensions/agentic/language/conceptual_metaphor/runners/conceptual_metaphor_spec.rb +133 -0
  87. data/spec/legion/extensions/agentic/language/frame_semantics/helpers/frame_engine_spec.rb +227 -0
  88. data/spec/legion/extensions/agentic/language/frame_semantics/helpers/frame_instance_spec.rb +83 -0
  89. data/spec/legion/extensions/agentic/language/frame_semantics/helpers/frame_spec.rb +213 -0
  90. data/spec/legion/extensions/agentic/language/frame_semantics/runners/frame_semantics_spec.rb +155 -0
  91. data/spec/legion/extensions/agentic/language/grammar/client_spec.rb +121 -0
  92. data/spec/legion/extensions/agentic/language/grammar/cognitive_grammar_spec.rb +18 -0
  93. data/spec/legion/extensions/agentic/language/grammar/helpers/constants_spec.rb +67 -0
  94. data/spec/legion/extensions/agentic/language/grammar/helpers/construal_spec.rb +124 -0
  95. data/spec/legion/extensions/agentic/language/grammar/helpers/construction_spec.rb +155 -0
  96. data/spec/legion/extensions/agentic/language/grammar/helpers/grammar_engine_spec.rb +206 -0
  97. data/spec/legion/extensions/agentic/language/grammar/runners/cognitive_grammar_spec.rb +189 -0
  98. data/spec/legion/extensions/agentic/language/inner_speech/client_spec.rb +39 -0
  99. data/spec/legion/extensions/agentic/language/inner_speech/helpers/inner_voice_spec.rb +185 -0
  100. data/spec/legion/extensions/agentic/language/inner_speech/helpers/speech_stream_spec.rb +158 -0
  101. data/spec/legion/extensions/agentic/language/inner_speech/helpers/utterance_spec.rb +121 -0
  102. data/spec/legion/extensions/agentic/language/inner_speech/runners/inner_speech_spec.rb +102 -0
  103. data/spec/legion/extensions/agentic/language/language/client_spec.rb +20 -0
  104. data/spec/legion/extensions/agentic/language/language/helpers/constants_spec.rb +31 -0
  105. data/spec/legion/extensions/agentic/language/language/helpers/lexicon_spec.rb +116 -0
  106. data/spec/legion/extensions/agentic/language/language/helpers/summarizer_spec.rb +224 -0
  107. data/spec/legion/extensions/agentic/language/language/runners/language_spec.rb +169 -0
  108. data/spec/legion/extensions/agentic/language/narrative_reasoning/client_spec.rb +19 -0
  109. data/spec/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_engine_spec.rb +182 -0
  110. data/spec/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_event_spec.rb +61 -0
  111. data/spec/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_spec.rb +168 -0
  112. data/spec/legion/extensions/agentic/language/narrative_reasoning/runners/narrative_reasoning_spec.rb +174 -0
  113. data/spec/legion/extensions/agentic/language/narrator/client_spec.rb +24 -0
  114. data/spec/legion/extensions/agentic/language/narrator/helpers/journal_spec.rb +95 -0
  115. data/spec/legion/extensions/agentic/language/narrator/helpers/llm_enhancer_spec.rb +107 -0
  116. data/spec/legion/extensions/agentic/language/narrator/helpers/prose_spec.rb +134 -0
  117. data/spec/legion/extensions/agentic/language/narrator/helpers/synthesizer_spec.rb +89 -0
  118. data/spec/legion/extensions/agentic/language/narrator/runners/narrator_llm_spec.rb +74 -0
  119. data/spec/legion/extensions/agentic/language/narrator/runners/narrator_spec.rb +126 -0
  120. data/spec/legion/extensions/agentic/language/pragmatic_inference/client_spec.rb +19 -0
  121. data/spec/legion/extensions/agentic/language/pragmatic_inference/helpers/constants_spec.rb +73 -0
  122. data/spec/legion/extensions/agentic/language/pragmatic_inference/helpers/pragmatic_engine_spec.rb +185 -0
  123. data/spec/legion/extensions/agentic/language/pragmatic_inference/helpers/utterance_spec.rb +111 -0
  124. data/spec/legion/extensions/agentic/language/pragmatic_inference/runners/pragmatic_inference_spec.rb +231 -0
  125. data/spec/spec_helper.rb +33 -0
  126. metadata +210 -0
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Language
7
+ module Narrator
8
+ module Helpers
9
+ module Prose
10
+ module_function
11
+
12
+ def emotion_phrase(valence: 0.0, arousal: 0.5, gut: nil)
13
+ emotion_label = emotion_label_for(valence)
14
+ arousal_label = arousal_label_for(arousal)
15
+ base = "I am #{arousal_label} and #{emotion_label}"
16
+ gut_note = gut_phrase(gut)
17
+ gut_note ? "#{base}. #{gut_note}" : base
18
+ end
19
+
20
+ def curiosity_phrase(intensity: 0.0, top_wonder: nil, wonder_count: 0)
21
+ label = curiosity_label_for(intensity)
22
+ base = "I am #{label}"
23
+ if top_wonder && wonder_count.positive?
24
+ "#{base}, with #{wonder_count} open #{'question' if wonder_count == 1}#{'questions' if wonder_count != 1}. " \
25
+ "Most pressing: \"#{top_wonder}\""
26
+ else
27
+ base
28
+ end
29
+ end
30
+
31
+ def prediction_phrase(confidence: 0.0, pending: 0, mode: nil)
32
+ label = confidence_label_for(confidence)
33
+ base = "I am #{label}"
34
+ base += " (#{mode} reasoning)" if mode
35
+ base += ", with #{pending} pending predictions" if pending.positive?
36
+ base
37
+ end
38
+
39
+ def attention_phrase(spotlight: 0, peripheral: 0, focused_domains: [])
40
+ parts = ["#{spotlight} signals in spotlight focus, #{peripheral} in peripheral awareness"]
41
+ parts << "manually focused on: #{focused_domains.join(', ')}" if focused_domains.any?
42
+ parts.join('. ')
43
+ end
44
+
45
+ def memory_phrase(trace_count: 0, health: 1.0)
46
+ health_label = health_label_for(health)
47
+ "Memory system #{health_label} with #{trace_count} active traces"
48
+ end
49
+
50
+ def reflection_phrase(health: 1.0, pending_adaptations: 0, recent_severity: nil)
51
+ label = health_label_for(health)
52
+ base = "Cognitive health: #{label}"
53
+ base += ". #{pending_adaptations} pending adaptation recommendations" if pending_adaptations.positive?
54
+ base += ". Most recent concern: #{recent_severity}" if recent_severity
55
+ base
56
+ end
57
+
58
+ def overall_narrative(sections)
59
+ "#{sections.compact.reject(&:empty?).join('. ')}."
60
+ end
61
+
62
+ def gut_phrase(gut)
63
+ return nil unless gut.is_a?(Hash)
64
+
65
+ signal = gut[:signal] || gut[:gut_signal]
66
+ return nil unless signal
67
+
68
+ if signal > 0.3
69
+ 'My gut says something important is happening'
70
+ elsif signal < -0.3
71
+ 'I have an uneasy feeling about the current situation'
72
+ end
73
+ end
74
+
75
+ def emotion_label_for(valence)
76
+ if valence > 0.5 then Constants::EMOTION_LABELS[:high_positive]
77
+ elsif valence > 0.1 then Constants::EMOTION_LABELS[:low_positive]
78
+ elsif valence > -0.1 then Constants::EMOTION_LABELS[:neutral]
79
+ elsif valence > -0.5 then Constants::EMOTION_LABELS[:low_negative]
80
+ else Constants::EMOTION_LABELS[:high_negative]
81
+ end
82
+ end
83
+
84
+ def arousal_label_for(arousal)
85
+ if arousal > 0.7 then Constants::AROUSAL_LABELS[:high]
86
+ elsif arousal > 0.4 then Constants::AROUSAL_LABELS[:medium]
87
+ elsif arousal > 0.1 then Constants::AROUSAL_LABELS[:low]
88
+ else Constants::AROUSAL_LABELS[:dormant]
89
+ end
90
+ end
91
+
92
+ def curiosity_label_for(intensity)
93
+ if intensity > 0.7 then Constants::CURIOSITY_LABELS[:high]
94
+ elsif intensity > 0.4 then Constants::CURIOSITY_LABELS[:medium]
95
+ elsif intensity > 0.1 then Constants::CURIOSITY_LABELS[:low]
96
+ else Constants::CURIOSITY_LABELS[:none]
97
+ end
98
+ end
99
+
100
+ def confidence_label_for(confidence)
101
+ if confidence > 0.7 then Constants::CONFIDENCE_LABELS[:high]
102
+ elsif confidence > 0.4 then Constants::CONFIDENCE_LABELS[:medium]
103
+ elsif confidence > 0.1 then Constants::CONFIDENCE_LABELS[:low]
104
+ else Constants::CONFIDENCE_LABELS[:none]
105
+ end
106
+ end
107
+
108
+ def health_label_for(health)
109
+ if health > 0.9 then Constants::HEALTH_LABELS[:excellent]
110
+ elsif health > 0.7 then Constants::HEALTH_LABELS[:good]
111
+ elsif health > 0.5 then Constants::HEALTH_LABELS[:fair]
112
+ elsif health > 0.3 then Constants::HEALTH_LABELS[:poor]
113
+ else Constants::HEALTH_LABELS[:critical]
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Language
7
+ module Narrator
8
+ module Helpers
9
+ module Synthesizer
10
+ module_function
11
+
12
+ def narrate(tick_results: {}, cognitive_state: {})
13
+ sections = build_sections(tick_results, cognitive_state)
14
+ narrative = Prose.overall_narrative(sections.values)
15
+ mood = infer_mood(tick_results, cognitive_state)
16
+
17
+ {
18
+ timestamp: Time.now.utc,
19
+ narrative: narrative,
20
+ sections: sections,
21
+ mood: mood,
22
+ tick_seq: tick_results[:tick_seq] || cognitive_state[:tick_seq]
23
+ }
24
+ end
25
+
26
+ def build_sections(tick_results, cognitive_state)
27
+ {
28
+ attention: synthesize_attention(tick_results, cognitive_state),
29
+ emotion: synthesize_emotion(tick_results, cognitive_state),
30
+ curiosity: synthesize_curiosity(tick_results, cognitive_state),
31
+ prediction: synthesize_prediction(tick_results, cognitive_state),
32
+ memory: synthesize_memory(tick_results, cognitive_state),
33
+ reflection: synthesize_reflection(cognitive_state)
34
+ }
35
+ end
36
+
37
+ def synthesize_attention(tick_results, cognitive_state)
38
+ attention = tick_results[:sensory_processing] || {}
39
+ focus = cognitive_state[:attention_status] || {}
40
+
41
+ Prose.attention_phrase(
42
+ spotlight: attention[:spotlight] || 0,
43
+ peripheral: attention[:peripheral] || 0,
44
+ focused_domains: extract_focused_domains(focus)
45
+ )
46
+ end
47
+
48
+ def synthesize_emotion(tick_results, cognitive_state)
49
+ valence_data = tick_results[:emotional_evaluation] || {}
50
+ gut_data = tick_results[:gut_instinct] || cognitive_state[:gut] || {}
51
+
52
+ Prose.emotion_phrase(
53
+ valence: valence_data[:valence] || cognitive_state.dig(:emotion, :valence) || 0.0,
54
+ arousal: valence_data[:arousal] || cognitive_state.dig(:emotion, :arousal) || 0.5,
55
+ gut: gut_data
56
+ )
57
+ end
58
+
59
+ def synthesize_curiosity(tick_results, cognitive_state)
60
+ curiosity_data = cognitive_state[:curiosity] || {}
61
+ wonder_data = tick_results[:working_memory_integration] || {}
62
+
63
+ Prose.curiosity_phrase(
64
+ intensity: curiosity_data[:intensity] || wonder_data[:curiosity_intensity] || 0.0,
65
+ top_wonder: extract_top_wonder(curiosity_data, wonder_data),
66
+ wonder_count: curiosity_data[:active_count] || wonder_data[:active_wonders] || 0
67
+ )
68
+ end
69
+
70
+ def synthesize_prediction(tick_results, cognitive_state)
71
+ pred_data = tick_results[:prediction_engine] || {}
72
+ pred_state = cognitive_state[:prediction] || {}
73
+
74
+ Prose.prediction_phrase(
75
+ confidence: pred_data[:confidence] || pred_state[:confidence] || 0.0,
76
+ pending: pred_state[:pending_count] || 0,
77
+ mode: pred_data[:mode] || pred_state[:mode]
78
+ )
79
+ end
80
+
81
+ def synthesize_memory(tick_results, cognitive_state)
82
+ memory_data = cognitive_state[:memory] || {}
83
+ consol = tick_results[:memory_consolidation] || {}
84
+
85
+ Prose.memory_phrase(
86
+ trace_count: memory_data[:trace_count] || consol[:remaining] || 0,
87
+ health: memory_data[:health] || 1.0
88
+ )
89
+ end
90
+
91
+ def synthesize_reflection(cognitive_state)
92
+ ref_data = cognitive_state[:reflection] || {}
93
+
94
+ Prose.reflection_phrase(
95
+ health: ref_data[:health] || 1.0,
96
+ pending_adaptations: ref_data[:pending_adaptations] || 0,
97
+ recent_severity: ref_data[:recent_severity]
98
+ )
99
+ end
100
+
101
+ def infer_mood(tick_results, cognitive_state)
102
+ valence = tick_results.dig(:emotional_evaluation, :valence) ||
103
+ cognitive_state.dig(:emotion, :valence) || 0.0
104
+ arousal = tick_results.dig(:emotional_evaluation, :arousal) ||
105
+ cognitive_state.dig(:emotion, :arousal) || 0.5
106
+
107
+ classify_mood(valence, arousal)
108
+ end
109
+
110
+ def classify_mood(valence, arousal)
111
+ if valence > 0.3 && arousal > 0.5 then :energized
112
+ elsif valence > 0.3 then :content
113
+ elsif valence < -0.3 && arousal > 0.5 then :anxious
114
+ elsif valence < -0.3 then :subdued
115
+ elsif arousal > 0.7 then :alert
116
+ elsif arousal < 0.2 then :dormant
117
+ else :neutral
118
+ end
119
+ end
120
+
121
+ def extract_focused_domains(focus)
122
+ manual = focus[:manual_focus]
123
+ return [] unless manual.is_a?(Hash)
124
+
125
+ manual.keys.map(&:to_s)
126
+ end
127
+
128
+ def extract_top_wonder(curiosity_data, wonder_data)
129
+ curiosity_data[:top_question] || wonder_data[:top_question] ||
130
+ (wonder_data[:top_wonder].is_a?(Hash) ? wonder_data[:top_wonder][:question] : nil)
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Language
7
+ module Narrator
8
+ module Runners
9
+ module Narrator
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def narrate(tick_results: {}, cognitive_state: {}, **)
14
+ entry = Helpers::Synthesizer.narrate(tick_results: tick_results, cognitive_state: cognitive_state)
15
+
16
+ if Helpers::LlmEnhancer.available?
17
+ sections_data = build_llm_sections_data(tick_results, cognitive_state, entry)
18
+ llm_result = Helpers::LlmEnhancer.narrate(sections_data: sections_data)
19
+ if llm_result
20
+ entry = entry.merge(narrative: llm_result, source: :llm)
21
+ journal.append(entry)
22
+ Legion::Logging.debug "[narrator] mood=#{entry[:mood]} source=llm sections=#{entry[:sections].keys.size}"
23
+ return {
24
+ narrative: entry[:narrative],
25
+ mood: entry[:mood],
26
+ timestamp: entry[:timestamp],
27
+ sections: entry[:sections],
28
+ source: :llm
29
+ }
30
+ end
31
+ end
32
+
33
+ journal.append(entry)
34
+ Legion::Logging.debug "[narrator] mood=#{entry[:mood]} sections=#{entry[:sections].keys.size}"
35
+
36
+ {
37
+ narrative: entry[:narrative],
38
+ mood: entry[:mood],
39
+ timestamp: entry[:timestamp],
40
+ sections: entry[:sections]
41
+ }
42
+ end
43
+
44
+ def recent_entries(limit: 10, **)
45
+ entries = journal.recent(limit: limit)
46
+ {
47
+ entries: entries.map { |e| format_entry(e) },
48
+ count: entries.size,
49
+ total: journal.size
50
+ }
51
+ end
52
+
53
+ def entries_since(since:, **)
54
+ timestamp = since.is_a?(Time) ? since : Time.parse(since.to_s)
55
+ entries = journal.since(timestamp)
56
+ {
57
+ entries: entries.map { |e| format_entry(e) },
58
+ count: entries.size,
59
+ since: timestamp
60
+ }
61
+ end
62
+
63
+ def mood_history(mood: nil, limit: 20, **)
64
+ entries = mood ? journal.by_mood(mood.to_sym) : journal.entries
65
+ recent = entries.last(limit)
66
+ {
67
+ entries: recent.map { |e| { timestamp: e[:timestamp], mood: e[:mood], narrative: e[:narrative] } },
68
+ count: recent.size,
69
+ mood: mood
70
+ }
71
+ end
72
+
73
+ def current_narrative(**)
74
+ entry = journal.entries.last
75
+ return { narrative: 'No cognitive activity recorded yet.', mood: :dormant } unless entry
76
+
77
+ {
78
+ narrative: entry[:narrative],
79
+ mood: entry[:mood],
80
+ timestamp: entry[:timestamp],
81
+ age_seconds: (Time.now.utc - entry[:timestamp]).round(1)
82
+ }
83
+ end
84
+
85
+ def narrator_stats(**)
86
+ stats = journal.stats
87
+ mood_summary = stats[:moods] || {}
88
+ dominant_mood = mood_summary.max_by { |_, count| count }&.first
89
+
90
+ {
91
+ journal_size: journal.size,
92
+ capacity: Helpers::Constants::MAX_JOURNAL_SIZE,
93
+ dominant_mood: dominant_mood,
94
+ mood_counts: mood_summary,
95
+ oldest: stats[:oldest],
96
+ newest: stats[:newest]
97
+ }
98
+ end
99
+
100
+ private
101
+
102
+ def journal
103
+ @journal ||= Helpers::Journal.new
104
+ end
105
+
106
+ def build_llm_sections_data(tick_results, cognitive_state, entry)
107
+ {
108
+ emotion: llm_emotion_data(tick_results, cognitive_state),
109
+ curiosity: llm_curiosity_data(tick_results, cognitive_state),
110
+ prediction: llm_prediction_data(tick_results, cognitive_state),
111
+ memory: llm_memory_data(tick_results, cognitive_state),
112
+ attention: llm_attention_data(tick_results, cognitive_state),
113
+ reflection: llm_reflection_data(cognitive_state),
114
+ mood: entry[:mood]
115
+ }
116
+ end
117
+
118
+ def llm_emotion_data(tick_results, cognitive_state)
119
+ v = tick_results[:emotional_evaluation] || {}
120
+ g = tick_results[:gut_instinct] || cognitive_state[:gut] || {}
121
+ {
122
+ valence: v[:valence] || cognitive_state.dig(:emotion, :valence) || 0.0,
123
+ arousal: v[:arousal] || cognitive_state.dig(:emotion, :arousal) || 0.5,
124
+ gut: g[:signal] || g[:gut_signal]
125
+ }
126
+ end
127
+
128
+ def llm_curiosity_data(tick_results, cognitive_state)
129
+ c = cognitive_state[:curiosity] || {}
130
+ w = tick_results[:working_memory_integration] || {}
131
+ {
132
+ intensity: c[:intensity] || w[:curiosity_intensity] || 0.0,
133
+ wonder_count: c[:active_count] || w[:active_wonders] || 0,
134
+ top_wonder: c[:top_question] || w[:top_question]
135
+ }
136
+ end
137
+
138
+ def llm_prediction_data(tick_results, cognitive_state)
139
+ p = tick_results[:prediction_engine] || {}
140
+ s = cognitive_state[:prediction] || {}
141
+ {
142
+ confidence: p[:confidence] || s[:confidence] || 0.0,
143
+ pending: s[:pending_count] || 0,
144
+ mode: p[:mode] || s[:mode]
145
+ }
146
+ end
147
+
148
+ def llm_memory_data(tick_results, cognitive_state)
149
+ m = cognitive_state[:memory] || {}
150
+ c = tick_results[:memory_consolidation] || {}
151
+ {
152
+ trace_count: m[:trace_count] || c[:remaining] || 0,
153
+ health: m[:health] || 1.0
154
+ }
155
+ end
156
+
157
+ def llm_attention_data(tick_results, cognitive_state)
158
+ a = tick_results[:sensory_processing] || {}
159
+ f = cognitive_state[:attention_status] || {}
160
+ {
161
+ spotlight: a[:spotlight] || 0,
162
+ peripheral: a[:peripheral] || 0,
163
+ focused_domains: extract_focused_domains_for_llm(f)
164
+ }
165
+ end
166
+
167
+ def llm_reflection_data(cognitive_state)
168
+ r = cognitive_state[:reflection] || {}
169
+ {
170
+ health: r[:health] || 1.0,
171
+ pending_adaptations: r[:pending_adaptations] || 0
172
+ }
173
+ end
174
+
175
+ def extract_focused_domains_for_llm(focus)
176
+ manual = focus[:manual_focus]
177
+ return [] unless manual.is_a?(Hash)
178
+
179
+ manual.keys.map(&:to_s)
180
+ end
181
+
182
+ def format_entry(entry)
183
+ {
184
+ timestamp: entry[:timestamp],
185
+ narrative: entry[:narrative],
186
+ mood: entry[:mood],
187
+ sections: entry[:sections]
188
+ }
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Language
7
+ module Narrator
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 'legion/extensions/agentic/language/narrator/version'
4
+ require 'legion/extensions/agentic/language/narrator/helpers/constants'
5
+ require 'legion/extensions/agentic/language/narrator/helpers/prose'
6
+ require 'legion/extensions/agentic/language/narrator/helpers/journal'
7
+ require 'legion/extensions/agentic/language/narrator/helpers/synthesizer'
8
+ require 'legion/extensions/agentic/language/narrator/helpers/llm_enhancer'
9
+ require 'legion/extensions/agentic/language/narrator/runners/narrator'
10
+ require 'legion/extensions/agentic/language/narrator/client'
11
+
12
+ module Legion
13
+ module Extensions
14
+ module Agentic
15
+ module Language
16
+ module Narrator
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/language/pragmatic_inference/helpers/constants'
4
+ require 'legion/extensions/agentic/language/pragmatic_inference/helpers/utterance'
5
+ require 'legion/extensions/agentic/language/pragmatic_inference/helpers/pragmatic_engine'
6
+ require 'legion/extensions/agentic/language/pragmatic_inference/runners/pragmatic_inference'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Agentic
11
+ module Language
12
+ module PragmaticInference
13
+ class Client
14
+ include Runners::PragmaticInference
15
+
16
+ def initialize(**)
17
+ @engine = Helpers::PragmaticEngine.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,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Language
7
+ module PragmaticInference
8
+ module Helpers
9
+ module Constants
10
+ MAX_UTTERANCES = 500
11
+ MAX_IMPLICATURES = 200
12
+ MAX_HISTORY = 300
13
+
14
+ DEFAULT_CONFIDENCE = 0.5
15
+ CONFIDENCE_FLOOR = 0.0
16
+ CONFIDENCE_CEILING = 1.0
17
+ REINFORCEMENT_RATE = 0.1
18
+ DECAY_RATE = 0.02
19
+
20
+ MAXIMS = %i[quality quantity relevance manner].freeze
21
+
22
+ VIOLATION_TYPES = %i[none flouting violating opting_out].freeze
23
+
24
+ SPEECH_ACTS = %i[assert question request promise warn inform suggest].freeze
25
+
26
+ MAXIM_DESCRIPTIONS = {
27
+ quality: 'be truthful, have evidence',
28
+ quantity: 'be informative, not more than needed',
29
+ relevance: 'be relevant to the conversation',
30
+ manner: 'be clear, brief, orderly, unambiguous'
31
+ }.freeze
32
+
33
+ module_function
34
+
35
+ def valid_maxim?(maxim)
36
+ MAXIMS.include?(maxim)
37
+ end
38
+
39
+ def valid_violation_type?(type)
40
+ VIOLATION_TYPES.include?(type)
41
+ end
42
+
43
+ def valid_speech_act?(act)
44
+ SPEECH_ACTS.include?(act)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end