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,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Language::Grammar::Helpers::Construction do
|
|
4
|
+
subject(:construction) do
|
|
5
|
+
described_class.new(
|
|
6
|
+
form: 'the cat',
|
|
7
|
+
meaning: 'definite feline entity',
|
|
8
|
+
expression_type: :nominal,
|
|
9
|
+
domain: 'linguistics'
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#initialize' do
|
|
14
|
+
it 'assigns a UUID id' do
|
|
15
|
+
expect(construction.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'stores form and meaning' do
|
|
19
|
+
expect(construction.form).to eq('the cat')
|
|
20
|
+
expect(construction.meaning).to eq('definite feline entity')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'symbolizes expression_type' do
|
|
24
|
+
expect(construction.expression_type).to eq(:nominal)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'defaults activation to DEFAULT_ACTIVATION' do
|
|
28
|
+
expect(construction.activation).to eq(0.3)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'starts with zero usage_count' do
|
|
32
|
+
expect(construction.usage_count).to eq(0)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'sets domain' do
|
|
36
|
+
expect(construction.domain).to eq('linguistics')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'sets created_at to a Time' do
|
|
40
|
+
expect(construction.created_at).to be_a(Time)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe '#use!' do
|
|
45
|
+
it 'increments usage_count' do
|
|
46
|
+
construction.use!
|
|
47
|
+
expect(construction.usage_count).to eq(1)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'boosts activation by ACTIVATION_BOOST' do
|
|
51
|
+
construction.use!
|
|
52
|
+
expect(construction.activation).to be_within(0.001).of(0.4)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'caps activation at 1.0' do
|
|
56
|
+
c = described_class.new(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.95)
|
|
57
|
+
c.use!
|
|
58
|
+
expect(c.activation).to eq(1.0)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'returns self for chaining' do
|
|
62
|
+
expect(construction.use!).to eq(construction)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'accumulates usage on repeated calls' do
|
|
66
|
+
5.times { construction.use! }
|
|
67
|
+
expect(construction.usage_count).to eq(5)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#decay!' do
|
|
72
|
+
it 'reduces activation by ACTIVATION_DECAY' do
|
|
73
|
+
construction.decay!
|
|
74
|
+
expect(construction.activation).to be_within(0.001).of(0.28)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'floors activation at 0.0' do
|
|
78
|
+
c = described_class.new(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.01)
|
|
79
|
+
c.decay!
|
|
80
|
+
expect(c.activation).to eq(0.0)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'returns self for chaining' do
|
|
84
|
+
expect(construction.decay!).to eq(construction)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe '#entrenched?' do
|
|
89
|
+
it 'returns false when activation is below threshold' do
|
|
90
|
+
expect(construction.entrenched?).to be false
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'returns true when activation reaches ENTRENCHMENT_THRESHOLD' do
|
|
94
|
+
c = described_class.new(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.8)
|
|
95
|
+
expect(c.entrenched?).to be true
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'returns true when activation exceeds threshold' do
|
|
99
|
+
c = described_class.new(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.95)
|
|
100
|
+
expect(c.entrenched?).to be true
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'becomes entrenched after enough use! calls' do
|
|
104
|
+
c = described_class.new(form: 'run', meaning: 'motion', expression_type: :relational, domain: 'motion')
|
|
105
|
+
# Start at 0.3, each use! adds 0.1 → need 5 uses to reach 0.8
|
|
106
|
+
5.times { c.use! }
|
|
107
|
+
expect(c.entrenched?).to be true
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe '#activation_label' do
|
|
112
|
+
it 'returns :ad_hoc for low activation' do
|
|
113
|
+
c = described_class.new(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.1)
|
|
114
|
+
expect(c.activation_label).to eq(:ad_hoc)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'returns :novel for activation 0.3' do
|
|
118
|
+
expect(construction.activation_label).to eq(:novel)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'returns :familiar for activation 0.5' do
|
|
122
|
+
c = described_class.new(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.5)
|
|
123
|
+
expect(c.activation_label).to eq(:familiar)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'returns :conventional for activation 0.7' do
|
|
127
|
+
c = described_class.new(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.7)
|
|
128
|
+
expect(c.activation_label).to eq(:conventional)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'returns :entrenched for activation >= 0.8' do
|
|
132
|
+
c = described_class.new(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.8)
|
|
133
|
+
expect(c.activation_label).to eq(:entrenched)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
describe '#to_h' do
|
|
138
|
+
it 'returns a hash with all expected keys' do
|
|
139
|
+
h = construction.to_h
|
|
140
|
+
expect(h).to include(:id, :form, :meaning, :expression_type, :activation, :usage_count, :domain, :entrenched,
|
|
141
|
+
:activation_label, :created_at)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'reflects current state' do
|
|
145
|
+
construction.use!
|
|
146
|
+
h = construction.to_h
|
|
147
|
+
expect(h[:usage_count]).to eq(1)
|
|
148
|
+
expect(h[:activation]).to be > 0.3
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'includes entrenched flag' do
|
|
152
|
+
expect(construction.to_h[:entrenched]).to be false
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Language::Grammar::Helpers::GrammarEngine do
|
|
4
|
+
subject(:engine) { described_class.new }
|
|
5
|
+
|
|
6
|
+
let(:construction_params) do
|
|
7
|
+
{ form: 'the bird', meaning: 'definite avian', expression_type: :nominal, domain: 'nature' }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
let(:construal_params) do
|
|
11
|
+
{ scene: 'bird on branch', perspective: 'ground-level', figure: 'bird', ground: 'branch' }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#create_construction' do
|
|
15
|
+
it 'returns a Construction instance' do
|
|
16
|
+
c = engine.create_construction(**construction_params)
|
|
17
|
+
expect(c).to be_a(Legion::Extensions::Agentic::Language::Grammar::Helpers::Construction)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'stores the construction internally' do
|
|
21
|
+
engine.create_construction(**construction_params)
|
|
22
|
+
expect(engine.to_h[:constructions_count]).to eq(1)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'assigns correct attributes' do
|
|
26
|
+
c = engine.create_construction(**construction_params)
|
|
27
|
+
expect(c.form).to eq('the bird')
|
|
28
|
+
expect(c.meaning).to eq('definite avian')
|
|
29
|
+
expect(c.expression_type).to eq(:nominal)
|
|
30
|
+
expect(c.domain).to eq('nature')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#create_construal' do
|
|
35
|
+
it 'returns a Construal instance' do
|
|
36
|
+
c = engine.create_construal(**construal_params)
|
|
37
|
+
expect(c).to be_a(Legion::Extensions::Agentic::Language::Grammar::Helpers::Construal)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'stores the construal' do
|
|
41
|
+
engine.create_construal(**construal_params)
|
|
42
|
+
expect(engine.to_h[:construals_count]).to eq(1)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'forwards all keyword args' do
|
|
46
|
+
c = engine.create_construal(**construal_params, specificity: :detailed, scope: :global, dynamicity: 0.8)
|
|
47
|
+
expect(c.specificity).to eq(:detailed)
|
|
48
|
+
expect(c.scope).to eq(:global)
|
|
49
|
+
expect(c.dynamicity).to eq(0.8)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe '#use_construction' do
|
|
54
|
+
it 'returns nil for unknown id' do
|
|
55
|
+
expect(engine.use_construction(construction_id: 'nonexistent')).to be_nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'increments usage_count' do
|
|
59
|
+
c = engine.create_construction(**construction_params)
|
|
60
|
+
engine.use_construction(construction_id: c.id)
|
|
61
|
+
expect(c.usage_count).to eq(1)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'returns the updated construction' do
|
|
65
|
+
c = engine.create_construction(**construction_params)
|
|
66
|
+
result = engine.use_construction(construction_id: c.id)
|
|
67
|
+
expect(result).to eq(c)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#construals_for_scene' do
|
|
72
|
+
it 'returns empty array when no construals exist' do
|
|
73
|
+
expect(engine.construals_for_scene(scene: 'nothing')).to eq([])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'returns only construals matching the scene' do
|
|
77
|
+
engine.create_construal(**construal_params)
|
|
78
|
+
engine.create_construal(scene: 'other scene', perspective: 'p', figure: 'f', ground: 'g')
|
|
79
|
+
results = engine.construals_for_scene(scene: 'bird on branch')
|
|
80
|
+
expect(results.size).to eq(1)
|
|
81
|
+
expect(results.first.scene).to eq('bird on branch')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'returns multiple construals for same scene (different perspectives)' do
|
|
85
|
+
engine.create_construal(**construal_params)
|
|
86
|
+
engine.create_construal(scene: 'bird on branch', perspective: 'aerial', figure: 'bird', ground: 'branch')
|
|
87
|
+
results = engine.construals_for_scene(scene: 'bird on branch')
|
|
88
|
+
expect(results.size).to eq(2)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe '#entrenched_constructions' do
|
|
93
|
+
it 'returns empty when none are entrenched' do
|
|
94
|
+
engine.create_construction(**construction_params)
|
|
95
|
+
expect(engine.entrenched_constructions).to be_empty
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'returns constructions once they reach entrenchment threshold via use!' do
|
|
99
|
+
c = engine.create_construction(**construction_params)
|
|
100
|
+
5.times { engine.use_construction(construction_id: c.id) }
|
|
101
|
+
expect(engine.entrenched_constructions).to include(c)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe '#constructions_by_domain' do
|
|
106
|
+
it 'returns only constructions in the specified domain' do
|
|
107
|
+
engine.create_construction(**construction_params)
|
|
108
|
+
engine.create_construction(form: 'run', meaning: 'motion', expression_type: :relational, domain: 'motion')
|
|
109
|
+
results = engine.constructions_by_domain(domain: 'nature')
|
|
110
|
+
expect(results.size).to eq(1)
|
|
111
|
+
expect(results.first.domain).to eq('nature')
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe '#constructions_by_type' do
|
|
116
|
+
it 'returns only constructions with the specified expression_type' do
|
|
117
|
+
engine.create_construction(**construction_params) # :nominal
|
|
118
|
+
engine.create_construction(form: 'runs', meaning: 'action', expression_type: :relational, domain: 'action')
|
|
119
|
+
results = engine.constructions_by_type(expression_type: :nominal)
|
|
120
|
+
expect(results.size).to eq(1)
|
|
121
|
+
expect(results.first.expression_type).to eq(:nominal)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it 'accepts string type and symbolizes it' do
|
|
125
|
+
engine.create_construction(**construction_params)
|
|
126
|
+
results = engine.constructions_by_type(expression_type: 'nominal')
|
|
127
|
+
expect(results.size).to eq(1)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe '#most_used' do
|
|
132
|
+
it 'returns constructions sorted by usage_count desc' do
|
|
133
|
+
c1 = engine.create_construction(**construction_params)
|
|
134
|
+
c2 = engine.create_construction(form: 'runs', meaning: 'action', expression_type: :relational, domain: 'action')
|
|
135
|
+
3.times { engine.use_construction(construction_id: c1.id) }
|
|
136
|
+
engine.use_construction(construction_id: c2.id)
|
|
137
|
+
top = engine.most_used(limit: 2)
|
|
138
|
+
expect(top.first).to eq(c1)
|
|
139
|
+
expect(top.last).to eq(c2)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'respects the limit' do
|
|
143
|
+
5.times do |i|
|
|
144
|
+
engine.create_construction(form: "form#{i}", meaning: "m#{i}", expression_type: :nominal, domain: 'd')
|
|
145
|
+
end
|
|
146
|
+
expect(engine.most_used(limit: 3).size).to eq(3)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
describe '#most_activated' do
|
|
151
|
+
it 'returns constructions sorted by activation desc' do
|
|
152
|
+
c1 = engine.create_construction(**construction_params)
|
|
153
|
+
c2 = engine.create_construction(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.8)
|
|
154
|
+
top = engine.most_activated(limit: 2)
|
|
155
|
+
expect(top.first).to eq(c2)
|
|
156
|
+
expect(top.last).to eq(c1)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
describe '#decay_all' do
|
|
161
|
+
it 'reduces activation of all constructions' do
|
|
162
|
+
c = engine.create_construction(**construction_params)
|
|
163
|
+
original = c.activation
|
|
164
|
+
engine.decay_all
|
|
165
|
+
expect(c.activation).to be < original
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
describe '#prune_inactive' do
|
|
170
|
+
it 'removes constructions with activation <= 0.05' do
|
|
171
|
+
c1 = engine.create_construction(**construction_params) # activation 0.3 — kept
|
|
172
|
+
c2 = engine.create_construction(form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd', activation: 0.05)
|
|
173
|
+
engine.prune_inactive
|
|
174
|
+
expect(engine.to_h[:constructions_count]).to eq(1)
|
|
175
|
+
expect(engine.constructions_by_domain(domain: 'nature')).to include(c1)
|
|
176
|
+
expect(engine.constructions_by_domain(domain: 'd')).not_to include(c2)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'does not remove constructions with activation above 0.05' do
|
|
180
|
+
engine.create_construction(**construction_params)
|
|
181
|
+
engine.prune_inactive
|
|
182
|
+
expect(engine.to_h[:constructions_count]).to eq(1)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
describe '#to_h' do
|
|
187
|
+
it 'returns counts' do
|
|
188
|
+
engine.create_construction(**construction_params)
|
|
189
|
+
engine.create_construal(**construal_params)
|
|
190
|
+
h = engine.to_h
|
|
191
|
+
expect(h[:constructions_count]).to eq(1)
|
|
192
|
+
expect(h[:construals_count]).to eq(1)
|
|
193
|
+
expect(h[:entrenched_count]).to eq(0)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
describe 'entrenchment via repeated use' do
|
|
198
|
+
it 'tracks a construction becoming entrenched after 5 uses from default activation' do
|
|
199
|
+
c = engine.create_construction(form: 'be', meaning: 'existence', expression_type: :relational, domain: 'core')
|
|
200
|
+
expect(c.entrenched?).to be false
|
|
201
|
+
5.times { engine.use_construction(construction_id: c.id) }
|
|
202
|
+
expect(c.entrenched?).to be true
|
|
203
|
+
expect(engine.entrenched_constructions).to include(c)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Language::Grammar::Runners::CognitiveGrammar do
|
|
4
|
+
subject(:runner) do
|
|
5
|
+
Class.new do
|
|
6
|
+
include Legion::Extensions::Agentic::Language::Grammar::Runners::CognitiveGrammar
|
|
7
|
+
|
|
8
|
+
def engine
|
|
9
|
+
@engine ||= Legion::Extensions::Agentic::Language::Grammar::Helpers::GrammarEngine.new
|
|
10
|
+
end
|
|
11
|
+
end.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
let(:construction_params) do
|
|
15
|
+
{ form: 'she runs', meaning: 'agent motion predication', expression_type: :clausal, domain: 'motion' }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe '#create_grammar_construction' do
|
|
19
|
+
it 'returns a hash with construction data' do
|
|
20
|
+
result = runner.create_grammar_construction(**construction_params)
|
|
21
|
+
expect(result).to include(:id, :form, :meaning, :expression_type, :activation, :usage_count)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'includes the provided form' do
|
|
25
|
+
result = runner.create_grammar_construction(**construction_params)
|
|
26
|
+
expect(result[:form]).to eq('she runs')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'starts with default activation' do
|
|
30
|
+
result = runner.create_grammar_construction(**construction_params)
|
|
31
|
+
expect(result[:activation]).to eq(0.3)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe '#create_grammar_construal' do
|
|
36
|
+
it 'returns a hash with construal data' do
|
|
37
|
+
result = runner.create_grammar_construal(
|
|
38
|
+
scene: 'bird on branch', perspective: 'worm-eye', figure: 'bird', ground: 'branch'
|
|
39
|
+
)
|
|
40
|
+
expect(result).to include(:id, :scene, :perspective, :figure, :ground, :specificity, :scope, :dynamicity)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'uses defaults for optional params' do
|
|
44
|
+
result = runner.create_grammar_construal(
|
|
45
|
+
scene: 's', perspective: 'p', figure: 'f', ground: 'g'
|
|
46
|
+
)
|
|
47
|
+
expect(result[:specificity]).to eq(:intermediate)
|
|
48
|
+
expect(result[:scope]).to eq(:local)
|
|
49
|
+
expect(result[:dynamicity]).to eq(0.5)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'respects provided specificity, scope, dynamicity' do
|
|
53
|
+
result = runner.create_grammar_construal(
|
|
54
|
+
scene: 's', perspective: 'p', figure: 'f', ground: 'g',
|
|
55
|
+
specificity: :detailed, scope: :global, dynamicity: 0.9
|
|
56
|
+
)
|
|
57
|
+
expect(result[:specificity]).to eq(:detailed)
|
|
58
|
+
expect(result[:scope]).to eq(:global)
|
|
59
|
+
expect(result[:dynamicity]).to eq(0.9)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe '#use_grammar_construction' do
|
|
64
|
+
it 'returns found: false for unknown id' do
|
|
65
|
+
result = runner.use_grammar_construction(construction_id: 'nonexistent')
|
|
66
|
+
expect(result[:found]).to be false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'returns found: true with updated construction' do
|
|
70
|
+
created = runner.create_grammar_construction(**construction_params)
|
|
71
|
+
result = runner.use_grammar_construction(construction_id: created[:id])
|
|
72
|
+
expect(result[:found]).to be true
|
|
73
|
+
expect(result[:construction][:usage_count]).to eq(1)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'increases activation on use' do
|
|
77
|
+
created = runner.create_grammar_construction(**construction_params)
|
|
78
|
+
result = runner.use_grammar_construction(construction_id: created[:id])
|
|
79
|
+
expect(result[:construction][:activation]).to be > created[:activation]
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe '#construals_for_scene_report' do
|
|
84
|
+
it 'returns empty construals for unknown scene' do
|
|
85
|
+
result = runner.construals_for_scene_report(scene: 'unknown')
|
|
86
|
+
expect(result[:count]).to eq(0)
|
|
87
|
+
expect(result[:construals]).to eq([])
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'returns matching construals' do
|
|
91
|
+
runner.create_grammar_construal(scene: 'cat on mat', perspective: 'p', figure: 'cat', ground: 'mat')
|
|
92
|
+
result = runner.construals_for_scene_report(scene: 'cat on mat')
|
|
93
|
+
expect(result[:count]).to eq(1)
|
|
94
|
+
expect(result[:construals].first[:figure]).to eq('cat')
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'includes the scene in the result' do
|
|
98
|
+
result = runner.construals_for_scene_report(scene: 'test scene')
|
|
99
|
+
expect(result[:scene]).to eq('test scene')
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe '#entrenched_constructions_report' do
|
|
104
|
+
it 'returns empty when none entrenched' do
|
|
105
|
+
runner.create_grammar_construction(**construction_params)
|
|
106
|
+
result = runner.entrenched_constructions_report
|
|
107
|
+
expect(result[:count]).to eq(0)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'returns entrenched constructions after enough uses' do
|
|
111
|
+
created = runner.create_grammar_construction(**construction_params)
|
|
112
|
+
5.times { runner.use_grammar_construction(construction_id: created[:id]) }
|
|
113
|
+
result = runner.entrenched_constructions_report
|
|
114
|
+
expect(result[:count]).to eq(1)
|
|
115
|
+
expect(result[:constructions].first[:entrenched]).to be true
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe '#constructions_by_domain_report' do
|
|
120
|
+
it 'returns constructions for the given domain' do
|
|
121
|
+
runner.create_grammar_construction(**construction_params) # domain: 'motion'
|
|
122
|
+
runner.create_grammar_construction(form: 'the', meaning: 'def', expression_type: :nominal, domain: 'grammar')
|
|
123
|
+
result = runner.constructions_by_domain_report(domain: 'motion')
|
|
124
|
+
expect(result[:domain]).to eq('motion')
|
|
125
|
+
expect(result[:count]).to eq(1)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe '#most_used_constructions' do
|
|
130
|
+
it 'returns constructions sorted by usage_count' do
|
|
131
|
+
c1 = runner.create_grammar_construction(**construction_params)
|
|
132
|
+
c2 = runner.create_grammar_construction(form: 'the', meaning: 'def', expression_type: :nominal, domain: 'grammar')
|
|
133
|
+
3.times { runner.use_grammar_construction(construction_id: c1[:id]) }
|
|
134
|
+
runner.use_grammar_construction(construction_id: c2[:id])
|
|
135
|
+
result = runner.most_used_constructions(limit: 2)
|
|
136
|
+
expect(result[:constructions].first[:form]).to eq('she runs')
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'defaults limit to 5' do
|
|
140
|
+
result = runner.most_used_constructions
|
|
141
|
+
expect(result[:limit]).to eq(5)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
describe '#update_cognitive_grammar' do
|
|
146
|
+
it 'returns stats with pruned count' do
|
|
147
|
+
runner.create_grammar_construction(**construction_params)
|
|
148
|
+
result = runner.update_cognitive_grammar
|
|
149
|
+
expect(result).to include(:pruned, :stats)
|
|
150
|
+
expect(result[:stats]).to include(:constructions_count, :construals_count)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'removes constructions whose activation decays to <= 0.05' do
|
|
154
|
+
# create with very low activation that will decay below threshold after one pass
|
|
155
|
+
c = runner.create_grammar_construction(
|
|
156
|
+
form: 'x', meaning: 'y', expression_type: :nominal, domain: 'd'
|
|
157
|
+
)
|
|
158
|
+
# Manually decay the underlying construction to near-zero
|
|
159
|
+
engine = runner.send(:engine)
|
|
160
|
+
const = engine.instance_variable_get(:@constructions)[c[:id]]
|
|
161
|
+
const.instance_variable_set(:@activation, 0.05)
|
|
162
|
+
result = runner.update_cognitive_grammar
|
|
163
|
+
expect(result[:pruned]).to be >= 1
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
describe '#cognitive_grammar_stats' do
|
|
168
|
+
it 'returns hash with counts' do
|
|
169
|
+
runner.create_grammar_construction(**construction_params)
|
|
170
|
+
runner.create_grammar_construal(scene: 's', perspective: 'p', figure: 'f', ground: 'g')
|
|
171
|
+
stats = runner.cognitive_grammar_stats
|
|
172
|
+
expect(stats[:constructions_count]).to eq(1)
|
|
173
|
+
expect(stats[:construals_count]).to eq(1)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
describe 'activation decay after update_cognitive_grammar' do
|
|
178
|
+
it 'reduces activation of all constructions' do
|
|
179
|
+
created = runner.create_grammar_construction(**construction_params)
|
|
180
|
+
original_activation = created[:activation]
|
|
181
|
+
runner.update_cognitive_grammar
|
|
182
|
+
runner.cognitive_grammar_stats
|
|
183
|
+
# activation should have decreased (decay was applied)
|
|
184
|
+
engine = runner.send(:engine)
|
|
185
|
+
const = engine.instance_variable_get(:@constructions)[created[:id]]
|
|
186
|
+
expect(const.activation).to be < original_activation
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Language::InnerSpeech::Client do
|
|
4
|
+
subject(:client) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it 'includes the InnerSpeech runner' do
|
|
7
|
+
expect(client).to respond_to(:inner_speak)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'accepts an injected voice' do
|
|
11
|
+
voice = Legion::Extensions::Agentic::Language::InnerSpeech::Helpers::InnerVoice.new
|
|
12
|
+
c = described_class.new(voice: voice)
|
|
13
|
+
expect(c.inner_speak(content: 'test')[:success]).to be true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'supports full conversation flow' do
|
|
17
|
+
client.inner_speak(content: 'hmm, what should I do?', mode: :questioning)
|
|
18
|
+
client.inner_plan(content: 'first, check the data')
|
|
19
|
+
client.inner_speak(content: 'that looks right', mode: :affirming)
|
|
20
|
+
|
|
21
|
+
stats = client.inner_speech_stats
|
|
22
|
+
expect(stats[:total_utterances]).to eq(3)
|
|
23
|
+
|
|
24
|
+
narrative = client.inner_narrative
|
|
25
|
+
expect(narrative[:narrative]).to include('check the data')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'supports debate and voice switching' do
|
|
29
|
+
client.switch_inner_voice(voice_type: :curious)
|
|
30
|
+
client.inner_debate(
|
|
31
|
+
content_a: 'this approach is fast',
|
|
32
|
+
content_b: 'but this approach is safe',
|
|
33
|
+
topic: :architecture
|
|
34
|
+
)
|
|
35
|
+
stats = client.inner_speech_stats
|
|
36
|
+
expect(stats[:active_voice]).to eq(:curious)
|
|
37
|
+
expect(stats[:total_utterances]).to eq(2)
|
|
38
|
+
end
|
|
39
|
+
end
|