lex-agentic-language 0.1.11 → 0.1.13
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 +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/lib/legion/extensions/agentic/language/conceptual_blending/runners/conceptual_blending.rb +2 -0
- data/lib/legion/extensions/agentic/language/grammar/runners/cognitive_grammar.rb +4 -0
- data/lib/legion/extensions/agentic/language/inner_speech/helpers/inner_voice.rb +10 -2
- data/lib/legion/extensions/agentic/language/inner_speech/helpers/speech_stream.rb +1 -1
- data/lib/legion/extensions/agentic/language/inner_speech/helpers/utterance.rb +10 -4
- data/lib/legion/extensions/agentic/language/narrator/runners/narrator.rb +16 -21
- data/lib/legion/extensions/agentic/language/version.rb +1 -1
- data/spec/legion/extensions/agentic/language/inner_speech/runners/inner_speech_spec.rb +1 -1
- data/spec/legion/extensions/agentic/language/narrator/runners/narrator_llm_spec.rb +24 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 15ffb64c29e1d193b5d11e2d295089b0aa6042babc91e3a2014662787c2cb843
|
|
4
|
+
data.tar.gz: a11688c4ba708f91fcd67518e02b105f70f48498ffdb7c4b3497e5ec7c62e975
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a79d9ec42c57d35f8d045707964baff90d4c0b8e91b393b5cf39b8f493182e09ee5a14fbb1b6da41250c005be7ae18d4147effc50060d02774e8b8ba5c91c754
|
|
7
|
+
data.tar.gz: 268b712c158554a3d0530b7e47197e9c8abb78d3ce060150be9db83d52f33e662eb47de47e0be45d8550a67df687c1b914d02a0e2ea74e02bb92a52d5b11b63f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.13] - 2026-10-29
|
|
4
|
+
### Fixed
|
|
5
|
+
- Only invoke narrator LLM enhancement for observations actually consumed by the narrative prompt, not dream provenance or phase bookkeeping.
|
|
6
|
+
- Preserve explicitly observed zero values while applying missing-value defaults only during prompt rendering.
|
|
7
|
+
|
|
8
|
+
## [0.1.12] - 2026-06-01
|
|
9
|
+
### Fixed
|
|
10
|
+
- `SpeechStream#append` used `:"utt_#{@counter}"` (interpolated Symbol) causing unbounded Symbol allocation — changed to String ID
|
|
11
|
+
- `Utterance#resolve_mode` and `#resolve_voice` called `to_sym` on arbitrary user input — replaced with safe string-based lookup returning only pre-existing constant Symbols
|
|
12
|
+
- `InnerVoice#switch_voice` called `to_sym` on user input — added `resolve_voice` helper with same safe pattern
|
|
13
|
+
- `CognitiveGrammar` runner called `.to_h` on nil when engine rejects invalid input — added nil guards returning structured error responses
|
|
14
|
+
|
|
3
15
|
## [0.1.11] - 2026-05-07
|
|
4
16
|
### Fixed
|
|
5
17
|
- Narrator LLM enhancement now sends native chat message payloads with system/user roles while preserving legacy session fallback for older test doubles.
|
data/README.md
CHANGED
data/lib/legion/extensions/agentic/language/conceptual_blending/runners/conceptual_blending.rb
CHANGED
|
@@ -40,6 +40,8 @@ module Legion
|
|
|
40
40
|
def create_blend(space_a_id:, space_b_id:, blend_type: :double_scope, **)
|
|
41
41
|
log.debug "[conceptual_blending] blend: a=#{space_a_id} b=#{space_b_id} type=#{blend_type}"
|
|
42
42
|
blend = engine.blend(space_a_id: space_a_id, space_b_id: space_b_id, blend_type: blend_type)
|
|
43
|
+
return { success: false, reason: :invalid_blend_type } unless blend
|
|
44
|
+
|
|
43
45
|
{ success: true, blend: blend.to_h }
|
|
44
46
|
rescue ArgumentError => e
|
|
45
47
|
log.debug "[conceptual_blending] blend failed: #{e.message}"
|
|
@@ -17,6 +17,8 @@ module Legion
|
|
|
17
17
|
expression_type: expression_type,
|
|
18
18
|
domain: domain
|
|
19
19
|
)
|
|
20
|
+
return { created: false, reason: :invalid_expression_type } unless construction
|
|
21
|
+
|
|
20
22
|
log.debug "[cognitive_grammar] created construction form=#{form} type=#{expression_type} domain=#{domain}"
|
|
21
23
|
construction.to_h
|
|
22
24
|
end
|
|
@@ -34,6 +36,8 @@ module Legion
|
|
|
34
36
|
dynamicity: dynamicity,
|
|
35
37
|
construction_id: construction_id
|
|
36
38
|
)
|
|
39
|
+
return { created: false, reason: :invalid_parameters } unless construal
|
|
40
|
+
|
|
37
41
|
log.debug "[cognitive_grammar] created construal scene=#{scene} figure=#{figure}"
|
|
38
42
|
construal.to_h
|
|
39
43
|
end
|
|
@@ -32,8 +32,8 @@ module Legion
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def switch_voice(voice:)
|
|
35
|
-
sym = voice
|
|
36
|
-
return nil unless
|
|
35
|
+
sym = resolve_voice(voice)
|
|
36
|
+
return nil unless sym
|
|
37
37
|
|
|
38
38
|
old_voice = @active_voice
|
|
39
39
|
@active_voice = sym
|
|
@@ -128,6 +128,14 @@ module Legion
|
|
|
128
128
|
|
|
129
129
|
private
|
|
130
130
|
|
|
131
|
+
def resolve_voice(voice)
|
|
132
|
+
return voice if voice.is_a?(Symbol) && VOICE_TYPES.include?(voice)
|
|
133
|
+
|
|
134
|
+
voice_str = voice.to_s
|
|
135
|
+
index = VOICE_TYPES.map(&:to_s).index(voice_str)
|
|
136
|
+
index ? VOICE_TYPES[index] : nil
|
|
137
|
+
end
|
|
138
|
+
|
|
131
139
|
def record_event(type, **details)
|
|
132
140
|
@history << { type: type, at: Time.now.utc }.merge(details)
|
|
133
141
|
@history.shift while @history.size > MAX_HISTORY
|
|
@@ -84,13 +84,19 @@ module Legion
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def resolve_mode(mode)
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
return mode if mode.is_a?(Symbol) && SPEECH_MODES.include?(mode)
|
|
88
|
+
|
|
89
|
+
mode_str = mode.to_s
|
|
90
|
+
index = SPEECH_MODES.map(&:to_s).index(mode_str)
|
|
91
|
+
index ? SPEECH_MODES[index] : :narrating
|
|
89
92
|
end
|
|
90
93
|
|
|
91
94
|
def resolve_voice(voice)
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
return voice if voice.is_a?(Symbol) && VOICE_TYPES.include?(voice)
|
|
96
|
+
|
|
97
|
+
voice_str = voice.to_s
|
|
98
|
+
index = VOICE_TYPES.map(&:to_s).index(voice_str)
|
|
99
|
+
index ? VOICE_TYPES[index] : :rational
|
|
94
100
|
end
|
|
95
101
|
end
|
|
96
102
|
end
|
|
@@ -13,8 +13,8 @@ module Legion
|
|
|
13
13
|
def narrate(tick_results: {}, cognitive_state: {}, **)
|
|
14
14
|
entry = Helpers::Synthesizer.narrate(tick_results: tick_results, cognitive_state: cognitive_state)
|
|
15
15
|
|
|
16
|
-
if Helpers::LlmEnhancer.available?
|
|
17
|
-
|
|
16
|
+
sections_data = build_llm_sections_data(tick_results, cognitive_state) if Helpers::LlmEnhancer.available?
|
|
17
|
+
if sections_data && meaningful_hash?(sections_data)
|
|
18
18
|
llm_result = Helpers::LlmEnhancer.narrate(sections_data: sections_data)
|
|
19
19
|
if llm_result
|
|
20
20
|
entry = entry.merge(narrative: llm_result, source: :llm)
|
|
@@ -103,10 +103,6 @@ module Legion
|
|
|
103
103
|
@journal ||= Helpers::Journal.new
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
def meaningful_for_llm?(tick_results, cognitive_state)
|
|
107
|
-
meaningful_hash?(tick_results) || meaningful_hash?(cognitive_state)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
106
|
def meaningful_hash?(value)
|
|
111
107
|
return false unless value.is_a?(Hash)
|
|
112
108
|
|
|
@@ -122,15 +118,14 @@ module Legion
|
|
|
122
118
|
end
|
|
123
119
|
end
|
|
124
120
|
|
|
125
|
-
def build_llm_sections_data(tick_results, cognitive_state
|
|
121
|
+
def build_llm_sections_data(tick_results, cognitive_state)
|
|
126
122
|
{
|
|
127
123
|
emotion: llm_emotion_data(tick_results, cognitive_state),
|
|
128
124
|
curiosity: llm_curiosity_data(tick_results, cognitive_state),
|
|
129
125
|
prediction: llm_prediction_data(tick_results, cognitive_state),
|
|
130
126
|
memory: llm_memory_data(tick_results, cognitive_state),
|
|
131
127
|
attention: llm_attention_data(tick_results, cognitive_state),
|
|
132
|
-
reflection: llm_reflection_data(cognitive_state)
|
|
133
|
-
mood: entry[:mood]
|
|
128
|
+
reflection: llm_reflection_data(cognitive_state)
|
|
134
129
|
}
|
|
135
130
|
end
|
|
136
131
|
|
|
@@ -138,8 +133,8 @@ module Legion
|
|
|
138
133
|
v = tick_results[:emotional_evaluation] || {}
|
|
139
134
|
g = tick_results[:gut_instinct] || cognitive_state[:gut] || {}
|
|
140
135
|
{
|
|
141
|
-
valence: v[:valence] || cognitive_state.dig(:emotion, :valence)
|
|
142
|
-
arousal: v[:arousal] || cognitive_state.dig(:emotion, :arousal)
|
|
136
|
+
valence: v[:valence] || cognitive_state.dig(:emotion, :valence),
|
|
137
|
+
arousal: v[:arousal] || cognitive_state.dig(:emotion, :arousal),
|
|
143
138
|
gut: g[:signal] || g[:gut_signal]
|
|
144
139
|
}
|
|
145
140
|
end
|
|
@@ -148,8 +143,8 @@ module Legion
|
|
|
148
143
|
c = cognitive_state[:curiosity] || {}
|
|
149
144
|
w = tick_results[:working_memory_integration] || {}
|
|
150
145
|
{
|
|
151
|
-
intensity: c[:intensity] || w[:curiosity_intensity]
|
|
152
|
-
wonder_count: c[:active_count] || w[:active_wonders]
|
|
146
|
+
intensity: c[:intensity] || w[:curiosity_intensity],
|
|
147
|
+
wonder_count: c[:active_count] || w[:active_wonders],
|
|
153
148
|
top_wonder: c[:top_question] || w[:top_question]
|
|
154
149
|
}
|
|
155
150
|
end
|
|
@@ -158,8 +153,8 @@ module Legion
|
|
|
158
153
|
p = tick_results[:prediction_engine] || {}
|
|
159
154
|
s = cognitive_state[:prediction] || {}
|
|
160
155
|
{
|
|
161
|
-
confidence: p[:confidence] || s[:confidence]
|
|
162
|
-
pending: s[:pending_count]
|
|
156
|
+
confidence: p[:confidence] || s[:confidence],
|
|
157
|
+
pending: s[:pending_count],
|
|
163
158
|
mode: p[:mode] || s[:mode]
|
|
164
159
|
}
|
|
165
160
|
end
|
|
@@ -168,8 +163,8 @@ module Legion
|
|
|
168
163
|
m = cognitive_state[:memory] || {}
|
|
169
164
|
c = tick_results[:memory_consolidation] || {}
|
|
170
165
|
{
|
|
171
|
-
trace_count: m[:trace_count] || c[:remaining]
|
|
172
|
-
health: m[:health]
|
|
166
|
+
trace_count: m[:trace_count] || c[:remaining],
|
|
167
|
+
health: m[:health]
|
|
173
168
|
}
|
|
174
169
|
end
|
|
175
170
|
|
|
@@ -177,8 +172,8 @@ module Legion
|
|
|
177
172
|
a = tick_results[:sensory_processing] || {}
|
|
178
173
|
f = cognitive_state[:attention_status] || {}
|
|
179
174
|
{
|
|
180
|
-
spotlight: a[:spotlight]
|
|
181
|
-
peripheral: a[:peripheral]
|
|
175
|
+
spotlight: a[:spotlight],
|
|
176
|
+
peripheral: a[:peripheral],
|
|
182
177
|
focused_domains: extract_focused_domains_for_llm(f)
|
|
183
178
|
}
|
|
184
179
|
end
|
|
@@ -186,8 +181,8 @@ module Legion
|
|
|
186
181
|
def llm_reflection_data(cognitive_state)
|
|
187
182
|
r = cognitive_state[:reflection] || {}
|
|
188
183
|
{
|
|
189
|
-
health: r[:health]
|
|
190
|
-
pending_adaptations: r[:pending_adaptations] || r[:unacted_count]
|
|
184
|
+
health: r[:health],
|
|
185
|
+
pending_adaptations: r[:pending_adaptations] || r[:unacted_count]
|
|
191
186
|
}
|
|
192
187
|
end
|
|
193
188
|
|
|
@@ -11,7 +11,7 @@ RSpec.describe Legion::Extensions::Agentic::Language::InnerSpeech::Runners::Inne
|
|
|
11
11
|
it 'creates an utterance' do
|
|
12
12
|
result = host.inner_speak(content: 'testing', mode: :planning)
|
|
13
13
|
expect(result[:success]).to be true
|
|
14
|
-
expect(result[:utterance_id]).to be_a(
|
|
14
|
+
expect(result[:utterance_id]).to be_a(String)
|
|
15
15
|
expect(result[:mode]).to eq(:planning)
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -47,6 +47,30 @@ RSpec.describe Legion::Extensions::Agentic::Language::Narrator::Runners::Narrato
|
|
|
47
47
|
expect(result).not_to have_key(:source)
|
|
48
48
|
expect(result[:narrative]).to be_a(String)
|
|
49
49
|
end
|
|
50
|
+
|
|
51
|
+
it 'does not invoke an LLM for dream provenance and phase bookkeeping alone' do
|
|
52
|
+
expect(chat_double).not_to receive(:ask)
|
|
53
|
+
|
|
54
|
+
result = client.narrate(
|
|
55
|
+
tick_results: { tick_seq: 42, memory_consolidation: { success: true },
|
|
56
|
+
emotional_evaluation: { status: :completed } },
|
|
57
|
+
cognitive_state: { source: :dream }
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
expect(result).not_to have_key(:source)
|
|
61
|
+
expect(client.current_narrative[:narrative]).to eq(result[:narrative])
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'narrates an explicitly observed zero rather than treating it as missing input' do
|
|
65
|
+
expect(chat_double).to receive(:ask).with(include('Valence: 0.0'))
|
|
66
|
+
|
|
67
|
+
result = client.narrate(
|
|
68
|
+
tick_results: { emotional_evaluation: { valence: 0.0 } },
|
|
69
|
+
cognitive_state: { source: :dream }
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
expect(result[:source]).to eq(:llm)
|
|
73
|
+
end
|
|
50
74
|
end
|
|
51
75
|
|
|
52
76
|
describe '#narrate with LLM available but narrate returns nil' do
|