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,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Language::InnerSpeech::Helpers::InnerVoice do
4
+ subject(:iv) { described_class.new }
5
+
6
+ describe '#initialize' do
7
+ it 'starts with rational voice' do
8
+ expect(iv.active_voice).to eq(:rational)
9
+ end
10
+
11
+ it 'has an empty stream' do
12
+ expect(iv.stream.size).to eq(0)
13
+ end
14
+ end
15
+
16
+ describe '#speak' do
17
+ it 'adds an utterance with active voice' do
18
+ u = iv.speak(content: 'hello world')
19
+ expect(u.voice).to eq(:rational)
20
+ expect(u.content).to eq('hello world')
21
+ end
22
+
23
+ it 'uses the specified mode' do
24
+ u = iv.speak(content: 'planning step', mode: :planning)
25
+ expect(u.mode).to eq(:planning)
26
+ end
27
+
28
+ it 'records event in history' do
29
+ iv.speak(content: 'test')
30
+ expect(iv.history.size).to eq(1)
31
+ end
32
+ end
33
+
34
+ describe '#switch_voice' do
35
+ it 'changes the active voice' do
36
+ iv.switch_voice(voice: :emotional)
37
+ expect(iv.active_voice).to eq(:emotional)
38
+ end
39
+
40
+ it 'rejects invalid voice' do
41
+ result = iv.switch_voice(voice: :bogus)
42
+ expect(result).to be_nil
43
+ expect(iv.active_voice).to eq(:rational)
44
+ end
45
+
46
+ it 'records event in history' do
47
+ iv.switch_voice(voice: :bold)
48
+ expect(iv.history.last[:type]).to eq(:switch_voice)
49
+ end
50
+ end
51
+
52
+ describe 'convenience methods' do
53
+ it '#plan creates a planning utterance' do
54
+ u = iv.plan(content: 'step one: gather data')
55
+ expect(u.mode).to eq(:planning)
56
+ end
57
+
58
+ it '#rehearse creates a rehearsal utterance' do
59
+ u = iv.rehearse(content: 'practice the response')
60
+ expect(u.mode).to eq(:rehearsal)
61
+ end
62
+
63
+ it '#question creates a questioning utterance' do
64
+ u = iv.question(content: 'what if this fails?')
65
+ expect(u.mode).to eq(:questioning)
66
+ end
67
+
68
+ it '#evaluate creates an evaluating utterance' do
69
+ u = iv.evaluate(content: 'that went well')
70
+ expect(u.mode).to eq(:evaluating)
71
+ end
72
+
73
+ it '#warn creates a high-urgency warning' do
74
+ u = iv.warn(content: 'careful with that')
75
+ expect(u.mode).to eq(:warning)
76
+ expect(u.urgency).to eq(0.8)
77
+ end
78
+ end
79
+
80
+ describe '#debate' do
81
+ it 'creates two utterances with different voices' do
82
+ results = iv.debate(
83
+ content_a: 'we should proceed',
84
+ content_b: 'but what about the risks?',
85
+ topic: :decision
86
+ )
87
+ expect(results.size).to eq(2)
88
+ expect(results.first.voice).to eq(:bold)
89
+ expect(results.last.voice).to eq(:cautious)
90
+ end
91
+
92
+ it 'restores the original voice after debate' do
93
+ iv.switch_voice(voice: :curious)
94
+ iv.debate(content_a: 'yes', content_b: 'no')
95
+ expect(iv.active_voice).to eq(:curious)
96
+ end
97
+ end
98
+
99
+ describe '#interrupt' do
100
+ it 'adds a high-urgency interruption' do
101
+ u = iv.interrupt(content: 'STOP!')
102
+ expect(u.urgency).to eq(0.9)
103
+ end
104
+
105
+ it 'records event in history' do
106
+ iv.interrupt(content: 'alert')
107
+ expect(iv.history.any? { |e| e[:type] == :interrupt }).to be true
108
+ end
109
+ end
110
+
111
+ describe '#set_speed' do
112
+ it 'sets automatic speed' do
113
+ iv.set_speed(mode: :automatic)
114
+ expect(iv.to_h[:speed]).to eq(3)
115
+ end
116
+
117
+ it 'sets controlled speed' do
118
+ iv.set_speed(mode: :controlled)
119
+ expect(iv.to_h[:speed]).to eq(1)
120
+ end
121
+
122
+ it 'sets egocentric speed' do
123
+ iv.set_speed(mode: :egocentric)
124
+ expect(iv.to_h[:speed]).to eq(0.5)
125
+ end
126
+ end
127
+
128
+ describe '#ruminating?' do
129
+ it 'delegates to stream' do
130
+ 4.times { iv.speak(content: 'same worry', topic: :anxiety) }
131
+ expect(iv.ruminating?).to be true
132
+ end
133
+ end
134
+
135
+ describe '#break_rumination' do
136
+ it 'redirects when ruminating' do
137
+ 4.times { iv.speak(content: 'same thing', topic: :worry) }
138
+ result = iv.break_rumination(redirect_topic: :work)
139
+ expect(result).to be true
140
+ end
141
+
142
+ it 'returns false when not ruminating' do
143
+ result = iv.break_rumination(redirect_topic: :work)
144
+ expect(result).to be false
145
+ end
146
+ end
147
+
148
+ describe '#recent_speech' do
149
+ it 'returns recent utterances' do
150
+ 3.times { |i| iv.speak(content: "thought #{i}") }
151
+ result = iv.recent_speech(count: 2)
152
+ expect(result.size).to eq(2)
153
+ end
154
+ end
155
+
156
+ describe '#narrative and #condensed_narrative' do
157
+ it 'returns full narrative' do
158
+ iv.speak(content: 'first')
159
+ iv.speak(content: 'second')
160
+ expect(iv.narrative).to eq('first second')
161
+ end
162
+
163
+ it 'returns condensed narrative from salient utterances' do
164
+ iv.speak(content: 'this is a longer thought that will be condensed', salience: 0.8)
165
+ expect(iv.condensed_narrative).to be_a(String)
166
+ end
167
+ end
168
+
169
+ describe '#tick' do
170
+ it 'decays stream and returns status' do
171
+ iv.speak(content: 'test')
172
+ result = iv.tick
173
+ expect(result).to include(:active_voice, :speed)
174
+ end
175
+ end
176
+
177
+ describe '#to_h' do
178
+ it 'returns expected keys' do
179
+ expect(iv.to_h).to include(
180
+ :active_voice, :speed, :stream_size, :ruminating,
181
+ :total_utterances, :history_size
182
+ )
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Language::InnerSpeech::Helpers::SpeechStream do
4
+ subject(:stream) { described_class.new }
5
+
6
+ describe '#append' do
7
+ it 'adds an utterance' do
8
+ u = stream.append(content: 'thinking about lunch')
9
+ expect(u).to be_a(Legion::Extensions::Agentic::Language::InnerSpeech::Helpers::Utterance)
10
+ expect(stream.size).to eq(1)
11
+ end
12
+
13
+ it 'enforces MAX_STREAM_LENGTH' do
14
+ 100.times { |i| stream.append(content: "thought #{i}") }
15
+ expect(stream.append(content: 'overflow')).to be_nil
16
+ end
17
+ end
18
+
19
+ describe '#current' do
20
+ it 'returns the most recent utterance' do
21
+ stream.append(content: 'first')
22
+ stream.append(content: 'second')
23
+ expect(stream.current.content).to eq('second')
24
+ end
25
+
26
+ it 'returns nil when empty' do
27
+ expect(stream.current).to be_nil
28
+ end
29
+ end
30
+
31
+ describe '#recent' do
32
+ it 'returns last N utterances' do
33
+ 5.times { |i| stream.append(content: "thought #{i}") }
34
+ result = stream.recent(count: 3)
35
+ expect(result.size).to eq(3)
36
+ end
37
+ end
38
+
39
+ describe '#by_mode' do
40
+ it 'filters by mode' do
41
+ stream.append(content: 'plan A', mode: :planning)
42
+ stream.append(content: 'what is that?', mode: :questioning)
43
+ expect(stream.by_mode(mode: :planning).size).to eq(1)
44
+ end
45
+ end
46
+
47
+ describe '#by_voice' do
48
+ it 'filters by voice' do
49
+ stream.append(content: 'careful now', voice: :cautious)
50
+ stream.append(content: 'go for it', voice: :bold)
51
+ expect(stream.by_voice(voice: :bold).size).to eq(1)
52
+ end
53
+ end
54
+
55
+ describe '#by_topic' do
56
+ it 'filters by topic' do
57
+ stream.append(content: 'safe code', topic: :safety)
58
+ stream.append(content: 'fast code', topic: :performance)
59
+ expect(stream.by_topic(topic: :safety).size).to eq(1)
60
+ end
61
+ end
62
+
63
+ describe '#salient' do
64
+ it 'returns salient utterances' do
65
+ stream.append(content: 'important', salience: 0.8)
66
+ stream.append(content: 'fading', salience: 0.1)
67
+ expect(stream.salient.size).to eq(1)
68
+ end
69
+ end
70
+
71
+ describe '#urgent' do
72
+ it 'returns urgent utterances' do
73
+ stream.append(content: 'critical', urgency: 0.9)
74
+ stream.append(content: 'calm', urgency: 0.2)
75
+ expect(stream.urgent.size).to eq(1)
76
+ end
77
+ end
78
+
79
+ describe '#ruminating?' do
80
+ it 'returns false with few utterances' do
81
+ stream.append(content: 'one', topic: :worry)
82
+ expect(stream.ruminating?).to be false
83
+ end
84
+
85
+ it 'detects rumination on same topic' do
86
+ 4.times { stream.append(content: 'still worried', topic: :worry) }
87
+ expect(stream.ruminating?).to be true
88
+ end
89
+
90
+ it 'returns false for varied topics' do
91
+ stream.append(content: 'a', topic: :work)
92
+ stream.append(content: 'b', topic: :food)
93
+ stream.append(content: 'c', topic: :fun)
94
+ expect(stream.ruminating?).to be false
95
+ end
96
+ end
97
+
98
+ describe '#rumination_topic' do
99
+ it 'returns the repeated topic' do
100
+ 4.times { stream.append(content: 'same thing', topic: :anxiety) }
101
+ expect(stream.rumination_topic).to eq(:anxiety)
102
+ end
103
+
104
+ it 'returns nil when not ruminating' do
105
+ expect(stream.rumination_topic).to be_nil
106
+ end
107
+ end
108
+
109
+ describe '#decay_all' do
110
+ it 'decays and prunes stale utterances' do
111
+ stream.append(content: 'will fade', salience: 0.06)
112
+ stream.decay_all
113
+ expect(stream.size).to eq(0)
114
+ end
115
+ end
116
+
117
+ describe '#interrupt' do
118
+ it 'adds a high-urgency utterance' do
119
+ u = stream.interrupt(content: 'ALERT!')
120
+ expect(u.urgency).to eq(0.9)
121
+ expect(u.mode).to eq(:warning)
122
+ end
123
+ end
124
+
125
+ describe '#condensed_stream' do
126
+ it 'returns condensed versions of salient utterances' do
127
+ stream.append(content: 'I need to think about this carefully', salience: 0.8)
128
+ condensed = stream.condensed_stream
129
+ expect(condensed).to be_an(Array)
130
+ expect(condensed.first.split.size).to be < 'I need to think about this carefully'.split.size
131
+ end
132
+ end
133
+
134
+ describe '#narrative' do
135
+ it 'joins all utterances into a string' do
136
+ stream.append(content: 'first thought')
137
+ stream.append(content: 'second thought')
138
+ expect(stream.narrative).to eq('first thought second thought')
139
+ end
140
+ end
141
+
142
+ describe '#clear' do
143
+ it 'empties the stream' do
144
+ stream.append(content: 'test')
145
+ stream.clear
146
+ expect(stream.size).to eq(0)
147
+ end
148
+ end
149
+
150
+ describe '#to_h' do
151
+ it 'returns expected keys' do
152
+ expect(stream.to_h).to include(
153
+ :size, :total_generated, :ruminating, :rumination_topic,
154
+ :salient_count, :urgent_count, :mode_distribution
155
+ )
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Language::InnerSpeech::Helpers::Utterance do
4
+ subject(:utterance) do
5
+ described_class.new(id: :utt_one, content: 'I should check the data first')
6
+ end
7
+
8
+ describe '#initialize' do
9
+ it 'sets content and defaults' do
10
+ expect(utterance.content).to eq('I should check the data first')
11
+ expect(utterance.mode).to eq(:narrating)
12
+ expect(utterance.voice).to eq(:rational)
13
+ expect(utterance.topic).to eq(:general)
14
+ expect(utterance.urgency).to eq(0.5)
15
+ expect(utterance.salience).to eq(0.5)
16
+ end
17
+
18
+ it 'accepts custom attributes' do
19
+ u = described_class.new(
20
+ id: :utt_two,
21
+ content: 'danger ahead',
22
+ mode: :warning,
23
+ voice: :cautious,
24
+ topic: :safety,
25
+ urgency: 0.9,
26
+ salience: 0.8,
27
+ source_subsystem: :emotion
28
+ )
29
+ expect(u.mode).to eq(:warning)
30
+ expect(u.voice).to eq(:cautious)
31
+ expect(u.topic).to eq(:safety)
32
+ expect(u.urgency).to eq(0.9)
33
+ expect(u.source_subsystem).to eq(:emotion)
34
+ end
35
+
36
+ it 'defaults invalid mode to :narrating' do
37
+ u = described_class.new(id: :x, content: 'test', mode: :bogus)
38
+ expect(u.mode).to eq(:narrating)
39
+ end
40
+
41
+ it 'defaults invalid voice to :rational' do
42
+ u = described_class.new(id: :x, content: 'test', voice: :bogus)
43
+ expect(u.voice).to eq(:rational)
44
+ end
45
+
46
+ it 'clamps urgency' do
47
+ u = described_class.new(id: :x, content: 'test', urgency: 2.0)
48
+ expect(u.urgency).to eq(1.0)
49
+ end
50
+ end
51
+
52
+ describe '#condensed_content' do
53
+ it 'returns abbreviated version' do
54
+ condensed = utterance.condensed_content
55
+ expect(condensed.split.size).to be < utterance.content.split.size
56
+ end
57
+
58
+ it 'keeps at least one word' do
59
+ u = described_class.new(id: :x, content: 'hello')
60
+ expect(u.condensed_content).to eq('hello')
61
+ end
62
+ end
63
+
64
+ describe '#urgent?' do
65
+ it 'returns true for high urgency' do
66
+ u = described_class.new(id: :x, content: 'test', urgency: 0.8)
67
+ expect(u.urgent?).to be true
68
+ end
69
+
70
+ it 'returns false for low urgency' do
71
+ u = described_class.new(id: :x, content: 'test', urgency: 0.2)
72
+ expect(u.urgent?).to be false
73
+ end
74
+ end
75
+
76
+ describe '#background?' do
77
+ it 'returns true for very low urgency' do
78
+ u = described_class.new(id: :x, content: 'test', urgency: 0.1)
79
+ expect(u.background?).to be true
80
+ end
81
+ end
82
+
83
+ describe '#decay_salience!' do
84
+ it 'reduces salience' do
85
+ original = utterance.salience
86
+ utterance.decay_salience!
87
+ expect(utterance.salience).to be < original
88
+ end
89
+
90
+ it 'does not go below floor' do
91
+ 20.times { utterance.decay_salience! }
92
+ expect(utterance.salience).to be >= 0.01
93
+ end
94
+ end
95
+
96
+ describe '#salient?' do
97
+ it 'returns true when salience is high' do
98
+ expect(utterance.salient?).to be true
99
+ end
100
+
101
+ it 'returns false after heavy decay' do
102
+ 15.times { utterance.decay_salience! }
103
+ expect(utterance.salient?).to be false
104
+ end
105
+ end
106
+
107
+ describe '#urgency_label' do
108
+ it 'returns a symbol' do
109
+ expect(utterance.urgency_label).to be_a(Symbol)
110
+ end
111
+ end
112
+
113
+ describe '#to_h' do
114
+ it 'returns expected keys' do
115
+ expect(utterance.to_h).to include(
116
+ :id, :content, :condensed, :mode, :voice, :topic,
117
+ :urgency, :salience, :source_subsystem, :urgency_label
118
+ )
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Language::InnerSpeech::Runners::InnerSpeech do
4
+ let(:host) do
5
+ obj = Object.new
6
+ obj.extend(described_class)
7
+ obj
8
+ end
9
+
10
+ describe '#inner_speak' do
11
+ it 'creates an utterance' do
12
+ result = host.inner_speak(content: 'testing', mode: :planning)
13
+ expect(result[:success]).to be true
14
+ expect(result[:utterance_id]).to be_a(Symbol)
15
+ expect(result[:mode]).to eq(:planning)
16
+ end
17
+ end
18
+
19
+ describe '#inner_plan' do
20
+ it 'creates a planning utterance' do
21
+ result = host.inner_plan(content: 'step one')
22
+ expect(result[:success]).to be true
23
+ end
24
+ end
25
+
26
+ describe '#inner_question' do
27
+ it 'creates a questioning utterance' do
28
+ result = host.inner_question(content: 'what if?')
29
+ expect(result[:success]).to be true
30
+ end
31
+ end
32
+
33
+ describe '#inner_debate' do
34
+ it 'creates a two-sided debate' do
35
+ result = host.inner_debate(content_a: 'yes', content_b: 'no', topic: :choice)
36
+ expect(result[:success]).to be true
37
+ expect(result[:count]).to eq(2)
38
+ end
39
+ end
40
+
41
+ describe '#switch_inner_voice' do
42
+ it 'switches voice' do
43
+ result = host.switch_inner_voice(voice_type: :emotional)
44
+ expect(result[:success]).to be true
45
+ expect(result[:active_voice]).to eq(:emotional)
46
+ end
47
+
48
+ it 'rejects invalid voice' do
49
+ result = host.switch_inner_voice(voice_type: :bogus)
50
+ expect(result[:success]).to be false
51
+ end
52
+ end
53
+
54
+ describe '#inner_interrupt' do
55
+ it 'creates an interruption' do
56
+ result = host.inner_interrupt(content: 'ALERT')
57
+ expect(result[:success]).to be true
58
+ end
59
+ end
60
+
61
+ describe '#break_inner_rumination' do
62
+ it 'attempts to break rumination' do
63
+ result = host.break_inner_rumination(redirect_topic: :work)
64
+ expect(result[:success]).to be true
65
+ expect(result[:rumination_broken]).to be false
66
+ end
67
+ end
68
+
69
+ describe '#recent_inner_speech' do
70
+ it 'returns recent speech' do
71
+ host.inner_speak(content: 'hello')
72
+ result = host.recent_inner_speech(count: 5)
73
+ expect(result[:success]).to be true
74
+ expect(result[:count]).to eq(1)
75
+ end
76
+ end
77
+
78
+ describe '#inner_narrative' do
79
+ it 'returns the narrative' do
80
+ host.inner_speak(content: 'first thought')
81
+ result = host.inner_narrative
82
+ expect(result[:success]).to be true
83
+ expect(result[:narrative]).to include('first thought')
84
+ end
85
+ end
86
+
87
+ describe '#update_inner_speech' do
88
+ it 'ticks the inner voice' do
89
+ result = host.update_inner_speech
90
+ expect(result[:success]).to be true
91
+ end
92
+ end
93
+
94
+ describe '#inner_speech_stats' do
95
+ it 'returns stats' do
96
+ result = host.inner_speech_stats
97
+ expect(result[:success]).to be true
98
+ expect(result).to have_key(:active_voice)
99
+ expect(result).to have_key(:stream_size)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Language::Language::Client do
4
+ describe '#initialize' do
5
+ it 'creates a default lexicon' do
6
+ client = described_class.new
7
+ expect(client.lexicon).to be_a(Legion::Extensions::Agentic::Language::Language::Helpers::Lexicon)
8
+ end
9
+
10
+ it 'accepts an injected lexicon' do
11
+ lexicon = Legion::Extensions::Agentic::Language::Language::Helpers::Lexicon.new
12
+ client = described_class.new(lexicon: lexicon)
13
+ expect(client.lexicon).to equal(lexicon)
14
+ end
15
+ end
16
+
17
+ it 'includes Runners::Language' do
18
+ expect(described_class.ancestors).to include(Legion::Extensions::Agentic::Language::Language::Runners::Language)
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Language::Language::Helpers::Constants do
4
+ it 'defines MAX_TRACES_PER_SUMMARY' do
5
+ expect(described_class::MAX_TRACES_PER_SUMMARY).to eq(50)
6
+ end
7
+
8
+ it 'defines MIN_SUMMARY_STRENGTH' do
9
+ expect(described_class::MIN_SUMMARY_STRENGTH).to eq(0.1)
10
+ end
11
+
12
+ it 'defines DEPTHS as frozen array of symbols' do
13
+ expect(described_class::DEPTHS).to eq(%i[brief standard detailed])
14
+ expect(described_class::DEPTHS).to be_frozen
15
+ end
16
+
17
+ it 'defines TYPE_PRIORITY as frozen hash' do
18
+ expect(described_class::TYPE_PRIORITY).to include(firmware: 0, sensory: 6)
19
+ expect(described_class::TYPE_PRIORITY).to be_frozen
20
+ end
21
+
22
+ it 'defines knowledge thresholds' do
23
+ expect(described_class::KNOWLEDGE_RICH).to eq(10)
24
+ expect(described_class::KNOWLEDGE_MODERATE).to eq(5)
25
+ expect(described_class::KNOWLEDGE_SPARSE).to eq(1)
26
+ end
27
+
28
+ it 'defines RESOLUTION_THRESHOLD' do
29
+ expect(described_class::RESOLUTION_THRESHOLD).to eq(3)
30
+ end
31
+ end