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
data/spec/legion/extensions/agentic/language/conceptual_blending/helpers/blending_engine_spec.rb
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Language::ConceptualBlending::Helpers::BlendingEngine do
|
|
4
|
+
subject(:engine) { described_class.new }
|
|
5
|
+
|
|
6
|
+
let(:space_a) do
|
|
7
|
+
engine.create_space(name: 'biology', domain: 'science').tap do |s|
|
|
8
|
+
s.add_element(name: 'virus', properties: { type: :pathogen })
|
|
9
|
+
s.add_element(name: 'host', properties: { type: :organism })
|
|
10
|
+
s.add_relation(from: 'virus', to: 'host', type: :infects)
|
|
11
|
+
s.add_relation(from: 'virus', to: 'host', type: :spreads)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
let(:space_b) do
|
|
16
|
+
engine.create_space(name: 'computing', domain: 'technology').tap do |s|
|
|
17
|
+
s.add_element(name: 'software', properties: { type: :program })
|
|
18
|
+
s.add_element(name: 'network', properties: { type: :infrastructure })
|
|
19
|
+
s.add_relation(from: 'software', to: 'network', type: :corrupts)
|
|
20
|
+
s.add_relation(from: 'software', to: 'network', type: :replicates)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe '#create_space' do
|
|
25
|
+
it 'returns a MentalSpace' do
|
|
26
|
+
space = engine.create_space(name: 'test', domain: 'misc')
|
|
27
|
+
expect(space).to be_a(Legion::Extensions::Agentic::Language::ConceptualBlending::Helpers::MentalSpace)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'stores space internally' do
|
|
31
|
+
space = engine.create_space(name: 'test', domain: 'misc')
|
|
32
|
+
expect(engine.to_h[:spaces_count]).to eq(1)
|
|
33
|
+
space # suppress unused warning
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe '#add_element_to_space' do
|
|
38
|
+
it 'adds element to the space' do
|
|
39
|
+
space = engine.create_space(name: 's', domain: 'd')
|
|
40
|
+
engine.add_element_to_space(space_id: space.id, name: 'elem', properties: { key: :val })
|
|
41
|
+
expect(space.elements['elem']).to eq({ key: :val })
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'raises ArgumentError for unknown space_id' do
|
|
45
|
+
expect do
|
|
46
|
+
engine.add_element_to_space(space_id: 'bad-id', name: 'x', properties: {})
|
|
47
|
+
end.to raise_error(ArgumentError, /not found/)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe '#add_relation_to_space' do
|
|
52
|
+
it 'adds relation to the space' do
|
|
53
|
+
space = engine.create_space(name: 's', domain: 'd')
|
|
54
|
+
engine.add_relation_to_space(space_id: space.id, from: 'a', to: 'b', type: :links)
|
|
55
|
+
expect(space.relations.first).to eq({ from: 'a', to: 'b', type: :links })
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'raises ArgumentError for unknown space_id' do
|
|
59
|
+
expect do
|
|
60
|
+
engine.add_relation_to_space(space_id: 'bad-id', from: 'a', to: 'b', type: :x)
|
|
61
|
+
end.to raise_error(ArgumentError, /not found/)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe '#blend' do
|
|
66
|
+
it 'creates a Blend from two spaces' do
|
|
67
|
+
blend = engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
68
|
+
expect(blend).to be_a(Legion::Extensions::Agentic::Language::ConceptualBlending::Helpers::Blend)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'sets both input space ids' do
|
|
72
|
+
blend = engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
73
|
+
expect(blend.input_space_ids).to contain_exactly(space_a.id, space_b.id)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'uses the given blend_type' do
|
|
77
|
+
blend = engine.blend(space_a_id: space_a.id, space_b_id: space_b.id, blend_type: :mirror)
|
|
78
|
+
expect(blend.blend_type).to eq(:mirror)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'extracts generic space with shared relation types' do
|
|
82
|
+
blend = engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
83
|
+
expect(blend.generic_space).to have_key(:shared_relation_types)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'merges elements from both spaces' do
|
|
87
|
+
blend = engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
88
|
+
merged = blend.blended_elements[:merged_elements]
|
|
89
|
+
expect(merged).to include('virus', 'software')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'generates emergent properties from cross-domain relations' do
|
|
93
|
+
blend = engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
94
|
+
expect(blend.blended_elements[:emergent_properties]).not_to be_empty
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'raises ArgumentError for unknown space ids' do
|
|
98
|
+
expect do
|
|
99
|
+
engine.blend(space_a_id: 'bad', space_b_id: space_b.id)
|
|
100
|
+
end.to raise_error(ArgumentError, /not found/)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe '#elaborate_blend' do
|
|
105
|
+
let(:blend) { engine.blend(space_a_id: space_a.id, space_b_id: space_b.id) }
|
|
106
|
+
|
|
107
|
+
it 'adds emergent property to blend' do
|
|
108
|
+
engine.elaborate_blend(blend_id: blend.id, emergent_property: 'computer_immune_system')
|
|
109
|
+
expect(blend.blended_elements[:emergent_properties]).to include('computer_immune_system')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'raises ArgumentError for unknown blend_id' do
|
|
113
|
+
expect do
|
|
114
|
+
engine.elaborate_blend(blend_id: 'bad', emergent_property: 'x')
|
|
115
|
+
end.to raise_error(ArgumentError, /not found/)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe '#compress_blend' do
|
|
120
|
+
let(:blend) { engine.blend(space_a_id: space_a.id, space_b_id: space_b.id) }
|
|
121
|
+
|
|
122
|
+
it 'reduces blend strength' do
|
|
123
|
+
original_strength = blend.strength
|
|
124
|
+
engine.compress_blend(blend_id: blend.id, removed_element: 'virus')
|
|
125
|
+
expect(blend.strength).to be < original_strength
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'raises ArgumentError for unknown blend_id' do
|
|
129
|
+
expect do
|
|
130
|
+
engine.compress_blend(blend_id: 'bad', removed_element: 'x')
|
|
131
|
+
end.to raise_error(ArgumentError, /not found/)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
describe '#find_blends' do
|
|
136
|
+
it 'finds blends involving spaces from the given domain' do
|
|
137
|
+
engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
138
|
+
results = engine.find_blends(domain: 'science')
|
|
139
|
+
expect(results).not_to be_empty
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'returns empty array when no matching domain' do
|
|
143
|
+
engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
144
|
+
results = engine.find_blends(domain: 'nonexistent')
|
|
145
|
+
expect(results).to eq([])
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
describe '#best_blends' do
|
|
150
|
+
it 'returns array of blends ordered by quality' do
|
|
151
|
+
engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
152
|
+
results = engine.best_blends(limit: 5)
|
|
153
|
+
expect(results).to be_an(Array)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'respects the limit' do
|
|
157
|
+
3.times { engine.blend(space_a_id: space_a.id, space_b_id: space_b.id) }
|
|
158
|
+
results = engine.best_blends(limit: 2)
|
|
159
|
+
expect(results.size).to be <= 2
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
describe '#blend_quality' do
|
|
164
|
+
let(:blend) { engine.blend(space_a_id: space_a.id, space_b_id: space_b.id) }
|
|
165
|
+
|
|
166
|
+
it 'returns quality assessment hash' do
|
|
167
|
+
result = engine.blend_quality(blend_id: blend.id)
|
|
168
|
+
expect(result).to include(:blend_id, :quality_score, :quality_label, :strength, :use_count, :stale)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it 'raises ArgumentError for unknown blend_id' do
|
|
172
|
+
expect do
|
|
173
|
+
engine.blend_quality(blend_id: 'bad')
|
|
174
|
+
end.to raise_error(ArgumentError, /not found/)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
describe '#decay_stale' do
|
|
179
|
+
it 'returns count of decayed blends' do
|
|
180
|
+
engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
181
|
+
count = engine.decay_stale
|
|
182
|
+
expect(count).to be_a(Integer)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
describe '#prune_weak' do
|
|
187
|
+
it 'returns count of pruned blends' do
|
|
188
|
+
pruned = engine.prune_weak
|
|
189
|
+
expect(pruned).to be_a(Integer)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it 'removes blends with strength below 0.1' do
|
|
193
|
+
blend = engine.blend(space_a_id: space_a.id, space_b_id: space_b.id)
|
|
194
|
+
blend.instance_variable_set(:@strength, 0.05)
|
|
195
|
+
engine.prune_weak
|
|
196
|
+
expect(engine.to_h[:blends_count]).to eq(0)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
describe '#to_h' do
|
|
201
|
+
it 'returns stats hash' do
|
|
202
|
+
result = engine.to_h
|
|
203
|
+
expect(result).to include(:spaces_count, :blends_count, :best_quality)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it 'reflects current state' do
|
|
207
|
+
engine.create_space(name: 's', domain: 'd')
|
|
208
|
+
expect(engine.to_h[:spaces_count]).to eq(1)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Language::ConceptualBlending::Helpers::MentalSpace do
|
|
4
|
+
subject(:space) { described_class.new(name: 'biology', domain: 'science') }
|
|
5
|
+
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'assigns a uuid id' do
|
|
8
|
+
expect(space.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'sets name and domain' do
|
|
12
|
+
expect(space.name).to eq('biology')
|
|
13
|
+
expect(space.domain).to eq('science')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'starts with empty elements and relations' do
|
|
17
|
+
expect(space.elements).to eq({})
|
|
18
|
+
expect(space.relations).to eq([])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'records created_at timestamp' do
|
|
22
|
+
expect(space.created_at).to be_a(Time)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#add_element' do
|
|
27
|
+
it 'stores element with properties' do
|
|
28
|
+
space.add_element(name: 'virus', properties: { type: :pathogen })
|
|
29
|
+
expect(space.elements['virus']).to eq({ type: :pathogen })
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'returns self for chaining' do
|
|
33
|
+
result = space.add_element(name: 'cell', properties: {})
|
|
34
|
+
expect(result).to eq(space)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'stores multiple elements' do
|
|
38
|
+
space.add_element(name: 'virus', properties: {})
|
|
39
|
+
space.add_element(name: 'host', properties: {})
|
|
40
|
+
expect(space.elements.size).to eq(2)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe '#add_relation' do
|
|
45
|
+
it 'stores relation hash' do
|
|
46
|
+
space.add_relation(from: 'virus', to: 'host', type: :infects)
|
|
47
|
+
expect(space.relations.first).to eq({ from: 'virus', to: 'host', type: :infects })
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'returns self for chaining' do
|
|
51
|
+
result = space.add_relation(from: 'a', to: 'b', type: :links)
|
|
52
|
+
expect(result).to eq(space)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'accumulates multiple relations' do
|
|
56
|
+
space.add_relation(from: 'a', to: 'b', type: :one)
|
|
57
|
+
space.add_relation(from: 'b', to: 'c', type: :two)
|
|
58
|
+
expect(space.relations.size).to eq(2)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe '#element_names' do
|
|
63
|
+
it 'returns array of element name keys' do
|
|
64
|
+
space.add_element(name: 'alpha', properties: {})
|
|
65
|
+
space.add_element(name: 'beta', properties: {})
|
|
66
|
+
expect(space.element_names).to contain_exactly('alpha', 'beta')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'returns empty array when no elements' do
|
|
70
|
+
expect(space.element_names).to eq([])
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe '#to_h' do
|
|
75
|
+
it 'returns a hash with all fields' do
|
|
76
|
+
result = space.to_h
|
|
77
|
+
expect(result).to include(:id, :name, :domain, :elements, :relations, :created_at)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'reflects current state' do
|
|
81
|
+
space.add_element(name: 'x', properties: { val: 1 })
|
|
82
|
+
expect(space.to_h[:elements]['x']).to eq({ val: 1 })
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/spec/legion/extensions/agentic/language/conceptual_blending/runners/conceptual_blending_spec.rb
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/agentic/language/conceptual_blending/client'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Legion::Extensions::Agentic::Language::ConceptualBlending::Runners::ConceptualBlending do
|
|
6
|
+
let(:client) { Legion::Extensions::Agentic::Language::ConceptualBlending::Client.new }
|
|
7
|
+
|
|
8
|
+
let(:space_a_id) do
|
|
9
|
+
result = client.create_mental_space(name: 'biology', domain: 'science')
|
|
10
|
+
result[:space][:id]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
let(:space_b_id) do
|
|
14
|
+
result = client.create_mental_space(name: 'computing', domain: 'technology')
|
|
15
|
+
result[:space][:id]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
before do
|
|
19
|
+
client.add_space_element(space_id: space_a_id, name: 'virus', properties: { type: :pathogen })
|
|
20
|
+
client.add_space_relation(space_id: space_a_id, from: 'virus', to: 'host', type: :infects)
|
|
21
|
+
client.add_space_element(space_id: space_b_id, name: 'software', properties: { type: :program })
|
|
22
|
+
client.add_space_relation(space_id: space_b_id, from: 'software', to: 'network', type: :corrupts)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#create_mental_space' do
|
|
26
|
+
it 'returns success with space hash' do
|
|
27
|
+
result = client.create_mental_space(name: 'test', domain: 'misc')
|
|
28
|
+
expect(result[:success]).to be true
|
|
29
|
+
expect(result[:space]).to include(:id, :name, :domain)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#add_space_element' do
|
|
34
|
+
it 'returns success with space_id and element name' do
|
|
35
|
+
result = client.add_space_element(space_id: space_a_id, name: 'new_elem', properties: {})
|
|
36
|
+
expect(result[:success]).to be true
|
|
37
|
+
expect(result[:element]).to eq('new_elem')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'returns failure for unknown space_id' do
|
|
41
|
+
result = client.add_space_element(space_id: 'bad-id', name: 'x', properties: {})
|
|
42
|
+
expect(result[:success]).to be false
|
|
43
|
+
expect(result[:error]).to include('not found')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe '#add_space_relation' do
|
|
48
|
+
it 'returns success with relation hash' do
|
|
49
|
+
result = client.add_space_relation(space_id: space_a_id, from: 'a', to: 'b', type: :links)
|
|
50
|
+
expect(result[:success]).to be true
|
|
51
|
+
expect(result[:relation]).to eq({ from: 'a', to: 'b', type: :links })
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'returns failure for unknown space_id' do
|
|
55
|
+
result = client.add_space_relation(space_id: 'bad', from: 'a', to: 'b', type: :x)
|
|
56
|
+
expect(result[:success]).to be false
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe '#create_blend' do
|
|
61
|
+
it 'returns success with blend hash' do
|
|
62
|
+
result = client.create_blend(space_a_id: space_a_id, space_b_id: space_b_id)
|
|
63
|
+
expect(result[:success]).to be true
|
|
64
|
+
expect(result[:blend]).to include(:id, :blend_type, :strength)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'uses provided blend_type' do
|
|
68
|
+
result = client.create_blend(space_a_id: space_a_id, space_b_id: space_b_id, blend_type: :mirror)
|
|
69
|
+
expect(result[:blend][:blend_type]).to eq(:mirror)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'returns failure for unknown space ids' do
|
|
73
|
+
result = client.create_blend(space_a_id: 'bad', space_b_id: space_b_id)
|
|
74
|
+
expect(result[:success]).to be false
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe '#elaborate_blend' do
|
|
79
|
+
let(:blend_id) do
|
|
80
|
+
result = client.create_blend(space_a_id: space_a_id, space_b_id: space_b_id)
|
|
81
|
+
result[:blend][:id]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'returns success with updated blend' do
|
|
85
|
+
result = client.elaborate_blend(blend_id: blend_id, emergent_property: 'antivirus_software')
|
|
86
|
+
expect(result[:success]).to be true
|
|
87
|
+
expect(result[:blend][:blended_elements][:emergent_properties]).to include('antivirus_software')
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'returns failure for unknown blend_id' do
|
|
91
|
+
result = client.elaborate_blend(blend_id: 'bad', emergent_property: 'x')
|
|
92
|
+
expect(result[:success]).to be false
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe '#compress_blend' do
|
|
97
|
+
let(:blend_id) do
|
|
98
|
+
result = client.create_blend(space_a_id: space_a_id, space_b_id: space_b_id)
|
|
99
|
+
result[:blend][:id]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'returns success with updated blend' do
|
|
103
|
+
result = client.compress_blend(blend_id: blend_id, removed_element: 'virus')
|
|
104
|
+
expect(result[:success]).to be true
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'returns failure for unknown blend_id' do
|
|
108
|
+
result = client.compress_blend(blend_id: 'bad', removed_element: 'x')
|
|
109
|
+
expect(result[:success]).to be false
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '#best_blends' do
|
|
114
|
+
it 'returns success with blends array' do
|
|
115
|
+
client.create_blend(space_a_id: space_a_id, space_b_id: space_b_id)
|
|
116
|
+
result = client.best_blends(limit: 5)
|
|
117
|
+
expect(result[:success]).to be true
|
|
118
|
+
expect(result[:blends]).to be_an(Array)
|
|
119
|
+
expect(result[:count]).to eq(result[:blends].size)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'returns empty array when no blends exist' do
|
|
123
|
+
fresh_client = Legion::Extensions::Agentic::Language::ConceptualBlending::Client.new
|
|
124
|
+
result = fresh_client.best_blends(limit: 5)
|
|
125
|
+
expect(result[:blends]).to eq([])
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe '#blend_quality' do
|
|
130
|
+
let(:blend_id) do
|
|
131
|
+
result = client.create_blend(space_a_id: space_a_id, space_b_id: space_b_id)
|
|
132
|
+
result[:blend][:id]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'returns quality assessment' do
|
|
136
|
+
result = client.blend_quality(blend_id: blend_id)
|
|
137
|
+
expect(result[:success]).to be true
|
|
138
|
+
expect(result).to include(:quality_score, :quality_label, :strength)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it 'returns failure for unknown blend_id' do
|
|
142
|
+
result = client.blend_quality(blend_id: 'bad')
|
|
143
|
+
expect(result[:success]).to be false
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
describe '#update_conceptual_blending' do
|
|
148
|
+
it 'returns success with decay and prune counts' do
|
|
149
|
+
result = client.update_conceptual_blending
|
|
150
|
+
expect(result[:success]).to be true
|
|
151
|
+
expect(result).to include(:decayed, :pruned)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
describe '#conceptual_blending_stats' do
|
|
156
|
+
it 'returns success with engine stats' do
|
|
157
|
+
result = client.conceptual_blending_stats
|
|
158
|
+
expect(result[:success]).to be true
|
|
159
|
+
expect(result).to include(:spaces_count, :blends_count)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Language::ConceptualMetaphor::Client do
|
|
4
|
+
subject(:client) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it 'creates a metaphor' do
|
|
7
|
+
result = client.create_metaphor(
|
|
8
|
+
source_domain: :money, target_domain: :time,
|
|
9
|
+
metaphor_type: :structural, mappings: { spend: :waste }
|
|
10
|
+
)
|
|
11
|
+
expect(result[:success]).to be true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'applies a metaphor' do
|
|
15
|
+
created = client.create_metaphor(
|
|
16
|
+
source_domain: :money, target_domain: :time,
|
|
17
|
+
metaphor_type: :structural, mappings: { spend: :waste }
|
|
18
|
+
)
|
|
19
|
+
result = client.apply_metaphor(
|
|
20
|
+
metaphor_id: created[:metaphor_id], source_concept: :spend
|
|
21
|
+
)
|
|
22
|
+
expect(result[:target_concept]).to eq(:waste)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'returns stats' do
|
|
26
|
+
result = client.conceptual_metaphor_stats
|
|
27
|
+
expect(result[:success]).to be true
|
|
28
|
+
end
|
|
29
|
+
end
|
data/spec/legion/extensions/agentic/language/conceptual_metaphor/helpers/metaphor_engine_spec.rb
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Language::ConceptualMetaphor::Helpers::MetaphorEngine do
|
|
4
|
+
subject(:engine) { described_class.new }
|
|
5
|
+
|
|
6
|
+
let(:metaphor) do
|
|
7
|
+
engine.create_metaphor(
|
|
8
|
+
source_domain: :money,
|
|
9
|
+
target_domain: :time,
|
|
10
|
+
metaphor_type: :structural,
|
|
11
|
+
mappings: { spend: :waste, save: :conserve }
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe '#create_metaphor' do
|
|
16
|
+
it 'creates and stores a metaphor' do
|
|
17
|
+
result = metaphor
|
|
18
|
+
expect(result).to be_a(Legion::Extensions::Agentic::Language::ConceptualMetaphor::Helpers::Metaphor)
|
|
19
|
+
expect(result.source_domain).to eq(:money)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'rejects invalid metaphor types' do
|
|
23
|
+
result = engine.create_metaphor(
|
|
24
|
+
source_domain: :a, target_domain: :b,
|
|
25
|
+
metaphor_type: :invalid, mappings: {}
|
|
26
|
+
)
|
|
27
|
+
expect(result[:success]).to be false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'records history' do
|
|
31
|
+
metaphor
|
|
32
|
+
expect(engine.history.size).to eq(1)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe '#apply_metaphor' do
|
|
37
|
+
it 'maps a source concept to target' do
|
|
38
|
+
result = engine.apply_metaphor(metaphor_id: metaphor.id, source_concept: :spend)
|
|
39
|
+
expect(result[:mapped]).to be true
|
|
40
|
+
expect(result[:target_concept]).to eq(:waste)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'returns found: false for unknown metaphor' do
|
|
44
|
+
result = engine.apply_metaphor(metaphor_id: 'nonexistent', source_concept: :spend)
|
|
45
|
+
expect(result[:found]).to be false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'returns mapped: false for unmapped concept' do
|
|
49
|
+
result = engine.apply_metaphor(metaphor_id: metaphor.id, source_concept: :borrow)
|
|
50
|
+
expect(result[:mapped]).to be false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe '#add_entailment' do
|
|
55
|
+
it 'adds entailment to metaphor' do
|
|
56
|
+
result = engine.add_entailment(metaphor_id: metaphor.id, entailment: 'time is valuable')
|
|
57
|
+
expect(result[:success]).to be true
|
|
58
|
+
expect(result[:entailment_count]).to eq(1)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'returns error for unknown metaphor' do
|
|
62
|
+
result = engine.add_entailment(metaphor_id: 'bad', entailment: 'test')
|
|
63
|
+
expect(result[:success]).to be false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe '#find_by_domain' do
|
|
68
|
+
it 'finds metaphors involving the domain' do
|
|
69
|
+
metaphor
|
|
70
|
+
results = engine.find_by_domain(domain: :time)
|
|
71
|
+
expect(results.size).to eq(1)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe '#find_by_source' do
|
|
76
|
+
it 'finds metaphors by source domain' do
|
|
77
|
+
metaphor
|
|
78
|
+
results = engine.find_by_source(source_domain: :money)
|
|
79
|
+
expect(results.size).to eq(1)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe '#find_by_target' do
|
|
84
|
+
it 'finds metaphors by target domain' do
|
|
85
|
+
metaphor
|
|
86
|
+
results = engine.find_by_target(target_domain: :time)
|
|
87
|
+
expect(results.size).to eq(1)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#conventional_metaphors' do
|
|
92
|
+
it 'returns metaphors with high conventionality' do
|
|
93
|
+
engine.create_metaphor(
|
|
94
|
+
source_domain: :war, target_domain: :argument,
|
|
95
|
+
metaphor_type: :structural, mappings: { attack: :criticize },
|
|
96
|
+
conventionality: 0.9
|
|
97
|
+
)
|
|
98
|
+
expect(engine.conventional_metaphors.size).to eq(1)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe '#novel_metaphors' do
|
|
103
|
+
it 'returns metaphors with low conventionality' do
|
|
104
|
+
engine.create_metaphor(
|
|
105
|
+
source_domain: :ocean, target_domain: :emotion,
|
|
106
|
+
metaphor_type: :ontological, mappings: { depth: :intensity },
|
|
107
|
+
conventionality: 0.1
|
|
108
|
+
)
|
|
109
|
+
expect(engine.novel_metaphors.size).to eq(1)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '#strongest' do
|
|
114
|
+
it 'returns metaphors sorted by strength' do
|
|
115
|
+
metaphor
|
|
116
|
+
engine.create_metaphor(
|
|
117
|
+
source_domain: :war, target_domain: :argument,
|
|
118
|
+
metaphor_type: :structural, mappings: {}, strength: 0.9
|
|
119
|
+
)
|
|
120
|
+
results = engine.strongest(limit: 2)
|
|
121
|
+
expect(results.first.strength).to be >= results.last.strength
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
describe '#by_type' do
|
|
126
|
+
it 'filters by metaphor type' do
|
|
127
|
+
metaphor
|
|
128
|
+
engine.create_metaphor(
|
|
129
|
+
source_domain: :container, target_domain: :mind,
|
|
130
|
+
metaphor_type: :ontological, mappings: { full: :knowledgeable }
|
|
131
|
+
)
|
|
132
|
+
structural = engine.by_type(metaphor_type: :structural)
|
|
133
|
+
expect(structural.size).to eq(1)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
describe '#decay_all' do
|
|
138
|
+
it 'reduces strength of all metaphors' do
|
|
139
|
+
original = metaphor.strength
|
|
140
|
+
engine.decay_all
|
|
141
|
+
expect(metaphor.strength).to be < original
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
describe '#prune_weak' do
|
|
146
|
+
it 'removes very weak metaphors' do
|
|
147
|
+
weak = engine.create_metaphor(
|
|
148
|
+
source_domain: :a, target_domain: :b,
|
|
149
|
+
metaphor_type: :structural, mappings: {}, strength: 0.03
|
|
150
|
+
)
|
|
151
|
+
30.times { weak.decay! }
|
|
152
|
+
pruned = engine.prune_weak
|
|
153
|
+
expect(pruned).to be >= 1
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
describe '#to_h' do
|
|
158
|
+
it 'returns summary stats' do
|
|
159
|
+
metaphor
|
|
160
|
+
stats = engine.to_h
|
|
161
|
+
expect(stats[:total_metaphors]).to eq(1)
|
|
162
|
+
expect(stats[:total_domains]).to eq(2)
|
|
163
|
+
expect(stats).to include(:type_counts, :conventional_count, :novel_count)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|