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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +5 -0
- data/LICENSE +21 -0
- data/README.md +13 -0
- data/lex-agentic-language.gemspec +30 -0
- data/lib/legion/extensions/agentic/language/conceptual_blending/client.rb +25 -0
- data/lib/legion/extensions/agentic/language/conceptual_blending/helpers/blend.rb +91 -0
- data/lib/legion/extensions/agentic/language/conceptual_blending/helpers/blending_engine.rb +171 -0
- data/lib/legion/extensions/agentic/language/conceptual_blending/helpers/constants.rb +35 -0
- data/lib/legion/extensions/agentic/language/conceptual_blending/helpers/mental_space.rb +51 -0
- data/lib/legion/extensions/agentic/language/conceptual_blending/runners/conceptual_blending.rb +106 -0
- data/lib/legion/extensions/agentic/language/conceptual_blending/version.rb +13 -0
- data/lib/legion/extensions/agentic/language/conceptual_blending.rb +20 -0
- data/lib/legion/extensions/agentic/language/conceptual_metaphor/client.rb +19 -0
- data/lib/legion/extensions/agentic/language/conceptual_metaphor/helpers/constants.rb +49 -0
- data/lib/legion/extensions/agentic/language/conceptual_metaphor/helpers/metaphor.rb +109 -0
- data/lib/legion/extensions/agentic/language/conceptual_metaphor/helpers/metaphor_engine.rb +154 -0
- data/lib/legion/extensions/agentic/language/conceptual_metaphor/runners/conceptual_metaphor.rb +107 -0
- data/lib/legion/extensions/agentic/language/conceptual_metaphor/version.rb +13 -0
- data/lib/legion/extensions/agentic/language/conceptual_metaphor.rb +19 -0
- data/lib/legion/extensions/agentic/language/frame_semantics/helpers/client.rb +23 -0
- data/lib/legion/extensions/agentic/language/frame_semantics/helpers/constants.rb +32 -0
- data/lib/legion/extensions/agentic/language/frame_semantics/helpers/frame.rb +109 -0
- data/lib/legion/extensions/agentic/language/frame_semantics/helpers/frame_engine.rb +139 -0
- data/lib/legion/extensions/agentic/language/frame_semantics/helpers/frame_instance.rb +51 -0
- data/lib/legion/extensions/agentic/language/frame_semantics/runners/frame_semantics.rb +108 -0
- data/lib/legion/extensions/agentic/language/frame_semantics/version.rb +13 -0
- data/lib/legion/extensions/agentic/language/frame_semantics.rb +20 -0
- data/lib/legion/extensions/agentic/language/grammar/client.rb +29 -0
- data/lib/legion/extensions/agentic/language/grammar/helpers/constants.rb +38 -0
- data/lib/legion/extensions/agentic/language/grammar/helpers/construal.rb +68 -0
- data/lib/legion/extensions/agentic/language/grammar/helpers/construction.rb +68 -0
- data/lib/legion/extensions/agentic/language/grammar/helpers/grammar_engine.rb +119 -0
- data/lib/legion/extensions/agentic/language/grammar/runners/cognitive_grammar.rb +100 -0
- data/lib/legion/extensions/agentic/language/grammar/version.rb +13 -0
- data/lib/legion/extensions/agentic/language/grammar.rb +20 -0
- data/lib/legion/extensions/agentic/language/inner_speech/client.rb +19 -0
- data/lib/legion/extensions/agentic/language/inner_speech/helpers/constants.rb +53 -0
- data/lib/legion/extensions/agentic/language/inner_speech/helpers/inner_voice.rb +141 -0
- data/lib/legion/extensions/agentic/language/inner_speech/helpers/speech_stream.rb +128 -0
- data/lib/legion/extensions/agentic/language/inner_speech/helpers/utterance.rb +90 -0
- data/lib/legion/extensions/agentic/language/inner_speech/runners/inner_speech.rb +87 -0
- data/lib/legion/extensions/agentic/language/inner_speech/version.rb +13 -0
- data/lib/legion/extensions/agentic/language/inner_speech.rb +20 -0
- data/lib/legion/extensions/agentic/language/language/client.rb +26 -0
- data/lib/legion/extensions/agentic/language/language/helpers/constants.rb +43 -0
- data/lib/legion/extensions/agentic/language/language/helpers/lexicon.rb +63 -0
- data/lib/legion/extensions/agentic/language/language/helpers/summarizer.rb +167 -0
- data/lib/legion/extensions/agentic/language/language/runners/language.rb +134 -0
- data/lib/legion/extensions/agentic/language/language/version.rb +13 -0
- data/lib/legion/extensions/agentic/language/language.rb +19 -0
- data/lib/legion/extensions/agentic/language/narrative_reasoning/client.rb +28 -0
- data/lib/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative.rb +123 -0
- data/lib/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_engine.rb +122 -0
- data/lib/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_event.rb +41 -0
- data/lib/legion/extensions/agentic/language/narrative_reasoning/runners/narrative_reasoning.rb +122 -0
- data/lib/legion/extensions/agentic/language/narrative_reasoning/version.rb +13 -0
- data/lib/legion/extensions/agentic/language/narrative_reasoning.rb +18 -0
- data/lib/legion/extensions/agentic/language/narrator/client.rb +27 -0
- data/lib/legion/extensions/agentic/language/narrator/helpers/constants.rb +69 -0
- data/lib/legion/extensions/agentic/language/narrator/helpers/journal.rb +68 -0
- data/lib/legion/extensions/agentic/language/narrator/helpers/llm_enhancer.rb +105 -0
- data/lib/legion/extensions/agentic/language/narrator/helpers/prose.rb +122 -0
- data/lib/legion/extensions/agentic/language/narrator/helpers/synthesizer.rb +138 -0
- data/lib/legion/extensions/agentic/language/narrator/runners/narrator.rb +196 -0
- data/lib/legion/extensions/agentic/language/narrator/version.rb +13 -0
- data/lib/legion/extensions/agentic/language/narrator.rb +21 -0
- data/lib/legion/extensions/agentic/language/pragmatic_inference/client.rb +28 -0
- data/lib/legion/extensions/agentic/language/pragmatic_inference/helpers/constants.rb +52 -0
- data/lib/legion/extensions/agentic/language/pragmatic_inference/helpers/pragmatic_engine.rb +164 -0
- data/lib/legion/extensions/agentic/language/pragmatic_inference/helpers/utterance.rb +84 -0
- data/lib/legion/extensions/agentic/language/pragmatic_inference/runners/pragmatic_inference.rb +136 -0
- data/lib/legion/extensions/agentic/language/pragmatic_inference/version.rb +13 -0
- data/lib/legion/extensions/agentic/language/pragmatic_inference.rb +18 -0
- data/lib/legion/extensions/agentic/language/version.rb +11 -0
- data/lib/legion/extensions/agentic/language.rb +28 -0
- data/spec/legion/extensions/agentic/language/conceptual_blending/client_spec.rb +78 -0
- data/spec/legion/extensions/agentic/language/conceptual_blending/helpers/blend_spec.rb +141 -0
- data/spec/legion/extensions/agentic/language/conceptual_blending/helpers/blending_engine_spec.rb +211 -0
- data/spec/legion/extensions/agentic/language/conceptual_blending/helpers/mental_space_spec.rb +85 -0
- data/spec/legion/extensions/agentic/language/conceptual_blending/runners/conceptual_blending_spec.rb +162 -0
- data/spec/legion/extensions/agentic/language/conceptual_metaphor/client_spec.rb +29 -0
- data/spec/legion/extensions/agentic/language/conceptual_metaphor/helpers/metaphor_engine_spec.rb +166 -0
- data/spec/legion/extensions/agentic/language/conceptual_metaphor/helpers/metaphor_spec.rb +133 -0
- data/spec/legion/extensions/agentic/language/conceptual_metaphor/runners/conceptual_metaphor_spec.rb +133 -0
- data/spec/legion/extensions/agentic/language/frame_semantics/helpers/frame_engine_spec.rb +227 -0
- data/spec/legion/extensions/agentic/language/frame_semantics/helpers/frame_instance_spec.rb +83 -0
- data/spec/legion/extensions/agentic/language/frame_semantics/helpers/frame_spec.rb +213 -0
- data/spec/legion/extensions/agentic/language/frame_semantics/runners/frame_semantics_spec.rb +155 -0
- data/spec/legion/extensions/agentic/language/grammar/client_spec.rb +121 -0
- data/spec/legion/extensions/agentic/language/grammar/cognitive_grammar_spec.rb +18 -0
- data/spec/legion/extensions/agentic/language/grammar/helpers/constants_spec.rb +67 -0
- data/spec/legion/extensions/agentic/language/grammar/helpers/construal_spec.rb +124 -0
- data/spec/legion/extensions/agentic/language/grammar/helpers/construction_spec.rb +155 -0
- data/spec/legion/extensions/agentic/language/grammar/helpers/grammar_engine_spec.rb +206 -0
- data/spec/legion/extensions/agentic/language/grammar/runners/cognitive_grammar_spec.rb +189 -0
- data/spec/legion/extensions/agentic/language/inner_speech/client_spec.rb +39 -0
- data/spec/legion/extensions/agentic/language/inner_speech/helpers/inner_voice_spec.rb +185 -0
- data/spec/legion/extensions/agentic/language/inner_speech/helpers/speech_stream_spec.rb +158 -0
- data/spec/legion/extensions/agentic/language/inner_speech/helpers/utterance_spec.rb +121 -0
- data/spec/legion/extensions/agentic/language/inner_speech/runners/inner_speech_spec.rb +102 -0
- data/spec/legion/extensions/agentic/language/language/client_spec.rb +20 -0
- data/spec/legion/extensions/agentic/language/language/helpers/constants_spec.rb +31 -0
- data/spec/legion/extensions/agentic/language/language/helpers/lexicon_spec.rb +116 -0
- data/spec/legion/extensions/agentic/language/language/helpers/summarizer_spec.rb +224 -0
- data/spec/legion/extensions/agentic/language/language/runners/language_spec.rb +169 -0
- data/spec/legion/extensions/agentic/language/narrative_reasoning/client_spec.rb +19 -0
- data/spec/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_engine_spec.rb +182 -0
- data/spec/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_event_spec.rb +61 -0
- data/spec/legion/extensions/agentic/language/narrative_reasoning/helpers/narrative_spec.rb +168 -0
- data/spec/legion/extensions/agentic/language/narrative_reasoning/runners/narrative_reasoning_spec.rb +174 -0
- data/spec/legion/extensions/agentic/language/narrator/client_spec.rb +24 -0
- data/spec/legion/extensions/agentic/language/narrator/helpers/journal_spec.rb +95 -0
- data/spec/legion/extensions/agentic/language/narrator/helpers/llm_enhancer_spec.rb +107 -0
- data/spec/legion/extensions/agentic/language/narrator/helpers/prose_spec.rb +134 -0
- data/spec/legion/extensions/agentic/language/narrator/helpers/synthesizer_spec.rb +89 -0
- data/spec/legion/extensions/agentic/language/narrator/runners/narrator_llm_spec.rb +74 -0
- data/spec/legion/extensions/agentic/language/narrator/runners/narrator_spec.rb +126 -0
- data/spec/legion/extensions/agentic/language/pragmatic_inference/client_spec.rb +19 -0
- data/spec/legion/extensions/agentic/language/pragmatic_inference/helpers/constants_spec.rb +73 -0
- data/spec/legion/extensions/agentic/language/pragmatic_inference/helpers/pragmatic_engine_spec.rb +185 -0
- data/spec/legion/extensions/agentic/language/pragmatic_inference/helpers/utterance_spec.rb +111 -0
- data/spec/legion/extensions/agentic/language/pragmatic_inference/runners/pragmatic_inference_spec.rb +231 -0
- data/spec/spec_helper.rb +33 -0
- 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
|