lex-agentic-self 0.1.1 → 0.1.3
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 +7 -0
- data/LICENSE +201 -21
- data/README.md +2 -2
- data/lex-agentic-self.gemspec +8 -0
- data/lib/legion/extensions/agentic/self/agency/runners/agency.rb +7 -7
- data/lib/legion/extensions/agentic/self/anchor/helpers/constants.rb +2 -2
- data/lib/legion/extensions/agentic/self/anosognosia/runners/anosognosia.rb +6 -6
- data/lib/legion/extensions/agentic/self/default_mode_network/runners/default_mode_network.rb +17 -17
- data/lib/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint.rb +7 -7
- data/lib/legion/extensions/agentic/self/identity/runners/identity.rb +8 -8
- data/lib/legion/extensions/agentic/self/metacognition/runners/metacognition.rb +1 -1
- data/lib/legion/extensions/agentic/self/metacognitive_monitoring/runners/metacognitive_monitoring.rb +11 -11
- data/lib/legion/extensions/agentic/self/narrative_arc/runners/narrative.rb +9 -9
- data/lib/legion/extensions/agentic/self/narrative_identity/runners/narrative_identity.rb +15 -15
- data/lib/legion/extensions/agentic/self/narrative_self/runners/narrative_self.rb +4 -4
- data/lib/legion/extensions/agentic/self/reflection/helpers/llm_enhancer.rb +26 -0
- data/lib/legion/extensions/agentic/self/reflection/runners/reflection.rb +4 -4
- data/lib/legion/extensions/agentic/self/self_talk/runners/self_talk.rb +11 -11
- data/lib/legion/extensions/agentic/self/version.rb +1 -1
- data/spec/legion/extensions/agentic/self/reflection/helpers/llm_enhancer_spec.rb +76 -0
- data/spec/spec_helper.rb +22 -17
- metadata +99 -1
data/lib/legion/extensions/agentic/self/metacognitive_monitoring/runners/metacognitive_monitoring.rb
CHANGED
|
@@ -26,8 +26,8 @@ module Legion
|
|
|
26
26
|
effort: effort
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
log.debug "[metacognitive] record_judgment type=#{type_sym} domain=#{domain} " \
|
|
30
|
+
"confidence=#{judgment.predicted_confidence.round(2)} id=#{judgment.id[0..7]}"
|
|
31
31
|
|
|
32
32
|
{ success: true, judgment_id: judgment.id, judgment: judgment.to_h }
|
|
33
33
|
end
|
|
@@ -37,12 +37,12 @@ module Legion
|
|
|
37
37
|
judgment = eng.resolve_judgment(judgment_id: judgment_id, actual_outcome: actual_outcome)
|
|
38
38
|
|
|
39
39
|
unless judgment
|
|
40
|
-
|
|
40
|
+
log.debug "[metacognitive] resolve_judgment not_found id=#{judgment_id[0..7]}"
|
|
41
41
|
return { success: false, error: :not_found }
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
log.info "[metacognitive] resolved judgment=#{judgment_id[0..7]} " \
|
|
45
|
+
"error=#{judgment.calibration_error&.round(3)}"
|
|
46
46
|
|
|
47
47
|
{ success: true, judgment_id: judgment_id, judgment: judgment.to_h }
|
|
48
48
|
end
|
|
@@ -51,7 +51,7 @@ module Legion
|
|
|
51
51
|
eng = engine || monitoring_engine
|
|
52
52
|
judgment = eng.feeling_of_knowing(domain: domain, query: query)
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
log.debug "[metacognitive] fok domain=#{domain} confidence=#{judgment.predicted_confidence.round(2)}"
|
|
55
55
|
|
|
56
56
|
{
|
|
57
57
|
success: true,
|
|
@@ -66,7 +66,7 @@ module Legion
|
|
|
66
66
|
eng = engine || monitoring_engine
|
|
67
67
|
judgment = eng.judgment_of_learning(domain: domain, content: content)
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
log.debug "[metacognitive] jol domain=#{domain} confidence=#{judgment.predicted_confidence.round(2)}"
|
|
70
70
|
|
|
71
71
|
{
|
|
72
72
|
success: true,
|
|
@@ -81,7 +81,7 @@ module Legion
|
|
|
81
81
|
eng = engine || monitoring_engine
|
|
82
82
|
findings = eng.detect_overconfidence
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
log.debug "[metacognitive] overconfidence_scan count=#{findings.size}"
|
|
85
85
|
|
|
86
86
|
{
|
|
87
87
|
success: true,
|
|
@@ -94,7 +94,7 @@ module Legion
|
|
|
94
94
|
eng = engine || monitoring_engine
|
|
95
95
|
findings = eng.detect_underconfidence
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
log.debug "[metacognitive] underconfidence_scan count=#{findings.size}"
|
|
98
98
|
|
|
99
99
|
{
|
|
100
100
|
success: true,
|
|
@@ -107,7 +107,7 @@ module Legion
|
|
|
107
107
|
eng = engine || monitoring_engine
|
|
108
108
|
report = eng.calibration_report
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
log.debug "[metacognitive] calibration_report domains=#{report[:by_domain].size}"
|
|
111
111
|
|
|
112
112
|
{ success: true, report: report }
|
|
113
113
|
end
|
|
@@ -116,7 +116,7 @@ module Legion
|
|
|
116
116
|
eng = engine || monitoring_engine
|
|
117
117
|
report = eng.monitoring_report
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
log.debug "[metacognitive] monitoring_report total=#{report[:total_judgments]}"
|
|
120
120
|
|
|
121
121
|
{ success: true, report: report }
|
|
122
122
|
end
|
|
@@ -16,11 +16,11 @@ module Legion
|
|
|
16
16
|
arc = eng.create_arc(title: title, domain: domain, initial_tension: initial_tension)
|
|
17
17
|
|
|
18
18
|
unless arc
|
|
19
|
-
|
|
19
|
+
log.warn "[narrative_arc] create_arc failed: engine at capacity (#{Helpers::Constants::MAX_ARCS})"
|
|
20
20
|
return { success: false, reason: :engine_at_capacity }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
log.debug "[narrative_arc] arc created: #{arc.arc_id[0..7]} title=#{title} domain=#{domain}"
|
|
24
24
|
{ success: true, arc_id: arc.arc_id, title: arc.title, arc_phase: arc.arc_phase,
|
|
25
25
|
tension_level: arc.tension_level }
|
|
26
26
|
end
|
|
@@ -39,11 +39,11 @@ module Legion
|
|
|
39
39
|
|
|
40
40
|
if result[:success]
|
|
41
41
|
arc = eng.get_arc(arc_id)
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
log.debug "[narrative_arc] beat added: arc=#{arc_id[0..7]} type=#{beat_type} " \
|
|
43
|
+
"phase=#{result[:arc_phase]} tension=#{result[:tension_level].round(2)}"
|
|
44
44
|
result[:dramatic_score] = arc.dramatic_score if arc
|
|
45
45
|
else
|
|
46
|
-
|
|
46
|
+
log.debug "[narrative_arc] add_beat failed: #{result[:reason]} arc=#{arc_id[0..7]}"
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
result
|
|
@@ -60,14 +60,14 @@ module Legion
|
|
|
60
60
|
def active_arcs(engine: nil, **)
|
|
61
61
|
eng = engine || arc_engine
|
|
62
62
|
arcs = eng.active_arcs
|
|
63
|
-
|
|
63
|
+
log.debug "[narrative_arc] active arcs count=#{arcs.size}"
|
|
64
64
|
{ arcs: arcs.map(&:to_h), count: arcs.size }
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def completed_arcs(engine: nil, **)
|
|
68
68
|
eng = engine || arc_engine
|
|
69
69
|
arcs = eng.completed_arcs
|
|
70
|
-
|
|
70
|
+
log.debug "[narrative_arc] completed arcs count=#{arcs.size}"
|
|
71
71
|
{ arcs: arcs.map(&:to_h), count: arcs.size }
|
|
72
72
|
end
|
|
73
73
|
|
|
@@ -76,14 +76,14 @@ module Legion
|
|
|
76
76
|
arc = eng.most_dramatic_arc
|
|
77
77
|
return { found: false } unless arc
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
log.debug "[narrative_arc] most dramatic: #{arc.arc_id[0..7]} score=#{arc.dramatic_score.round(2)}"
|
|
80
80
|
{ found: true, arc: arc.to_h }
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def arc_report(engine: nil, **)
|
|
84
84
|
eng = engine || arc_engine
|
|
85
85
|
report = eng.arc_report
|
|
86
|
-
|
|
86
|
+
log.debug "[narrative_arc] arc_report total=#{report[:total_arcs]} patterns=#{report[:patterns].inspect}"
|
|
87
87
|
{ success: true, report: report }
|
|
88
88
|
end
|
|
89
89
|
|
|
@@ -18,7 +18,7 @@ module Legion
|
|
|
18
18
|
content: content, episode_type: episode_type,
|
|
19
19
|
emotional_valence: emotional_valence, significance: significance, domain: domain
|
|
20
20
|
)
|
|
21
|
-
|
|
21
|
+
log.debug "[narrative_identity] recorded episode #{episode.id[0..7]} type=#{episode_type}"
|
|
22
22
|
{ success: true, episode: episode.to_h }
|
|
23
23
|
rescue StandardError => e
|
|
24
24
|
{ success: false, error: e.message }
|
|
@@ -26,7 +26,7 @@ module Legion
|
|
|
26
26
|
|
|
27
27
|
def assign_episode_to_chapter(episode_id:, chapter_id:, engine: nil, **)
|
|
28
28
|
result = resolve_engine(engine).assign_to_chapter(episode_id: episode_id, chapter_id: chapter_id)
|
|
29
|
-
|
|
29
|
+
log.debug "[narrative_identity] assign episode=#{episode_id[0..7]} ok=#{result}"
|
|
30
30
|
{ success: result, episode_id: episode_id, chapter_id: chapter_id }
|
|
31
31
|
rescue StandardError => e
|
|
32
32
|
{ success: false, error: e.message }
|
|
@@ -37,7 +37,7 @@ module Legion
|
|
|
37
37
|
Helpers::Constants::CHAPTER_LABELS.include?(label)
|
|
38
38
|
|
|
39
39
|
chapter = resolve_engine(engine).create_chapter(title: title, label: label)
|
|
40
|
-
|
|
40
|
+
log.debug "[narrative_identity] created chapter #{chapter.id[0..7]} label=#{label}"
|
|
41
41
|
{ success: true, chapter: chapter.to_h }
|
|
42
42
|
rescue StandardError => e
|
|
43
43
|
{ success: false, error: e.message }
|
|
@@ -45,7 +45,7 @@ module Legion
|
|
|
45
45
|
|
|
46
46
|
def close_chapter(chapter_id:, engine: nil, **)
|
|
47
47
|
result = resolve_engine(engine).close_chapter(chapter_id: chapter_id)
|
|
48
|
-
|
|
48
|
+
log.debug "[narrative_identity] close_chapter #{chapter_id[0..7]} ok=#{result}"
|
|
49
49
|
{ success: result, chapter_id: chapter_id }
|
|
50
50
|
rescue StandardError => e
|
|
51
51
|
{ success: false, error: e.message }
|
|
@@ -56,7 +56,7 @@ module Legion
|
|
|
56
56
|
Helpers::Constants::THEME_TYPES.include?(theme_type)
|
|
57
57
|
|
|
58
58
|
theme = resolve_engine(engine).add_theme(name: name, theme_type: theme_type)
|
|
59
|
-
|
|
59
|
+
log.debug "[narrative_identity] added theme #{theme.id[0..7]} type=#{theme_type}"
|
|
60
60
|
{ success: true, theme: theme.to_h }
|
|
61
61
|
rescue StandardError => e
|
|
62
62
|
{ success: false, error: e.message }
|
|
@@ -64,7 +64,7 @@ module Legion
|
|
|
64
64
|
|
|
65
65
|
def link_theme(episode_id:, theme_id:, engine: nil, **)
|
|
66
66
|
result = resolve_engine(engine).link_theme(episode_id: episode_id, theme_id: theme_id)
|
|
67
|
-
|
|
67
|
+
log.debug "[narrative_identity] link theme=#{theme_id[0..7]} episode=#{episode_id[0..7]} ok=#{result}"
|
|
68
68
|
{ success: result, episode_id: episode_id, theme_id: theme_id }
|
|
69
69
|
rescue StandardError => e
|
|
70
70
|
{ success: false, error: e.message }
|
|
@@ -72,7 +72,7 @@ module Legion
|
|
|
72
72
|
|
|
73
73
|
def reinforce_theme(theme_id:, amount:, engine: nil, **)
|
|
74
74
|
result = resolve_engine(engine).reinforce_theme(theme_id: theme_id, amount: amount)
|
|
75
|
-
|
|
75
|
+
log.debug "[narrative_identity] reinforce theme=#{theme_id[0..7]} amount=#{amount} ok=#{result}"
|
|
76
76
|
{ success: result, theme_id: theme_id, amount: amount }
|
|
77
77
|
rescue StandardError => e
|
|
78
78
|
{ success: false, error: e.message }
|
|
@@ -80,7 +80,7 @@ module Legion
|
|
|
80
80
|
|
|
81
81
|
def narrative_coherence(engine: nil, **)
|
|
82
82
|
score = resolve_engine(engine).narrative_coherence
|
|
83
|
-
|
|
83
|
+
log.debug "[narrative_identity] coherence=#{score}"
|
|
84
84
|
{ success: true, coherence: score }
|
|
85
85
|
rescue StandardError => e
|
|
86
86
|
{ success: false, error: e.message }
|
|
@@ -88,7 +88,7 @@ module Legion
|
|
|
88
88
|
|
|
89
89
|
def identity_summary(engine: nil, **)
|
|
90
90
|
summary = resolve_engine(engine).identity_summary
|
|
91
|
-
|
|
91
|
+
log.debug '[narrative_identity] identity_summary requested'
|
|
92
92
|
{ success: true, summary: summary }
|
|
93
93
|
rescue StandardError => e
|
|
94
94
|
{ success: false, error: e.message }
|
|
@@ -96,7 +96,7 @@ module Legion
|
|
|
96
96
|
|
|
97
97
|
def life_story(engine: nil, **)
|
|
98
98
|
story = resolve_engine(engine).life_story
|
|
99
|
-
|
|
99
|
+
log.debug "[narrative_identity] life_story chapters=#{story.size}"
|
|
100
100
|
{ success: true, life_story: story }
|
|
101
101
|
rescue StandardError => e
|
|
102
102
|
{ success: false, error: e.message }
|
|
@@ -104,7 +104,7 @@ module Legion
|
|
|
104
104
|
|
|
105
105
|
def most_defining_episodes(limit: 5, engine: nil, **)
|
|
106
106
|
episodes = resolve_engine(engine).most_defining_episodes(limit: limit)
|
|
107
|
-
|
|
107
|
+
log.debug "[narrative_identity] defining_episodes count=#{episodes.size}"
|
|
108
108
|
{ success: true, episodes: episodes.map(&:to_h) }
|
|
109
109
|
rescue StandardError => e
|
|
110
110
|
{ success: false, error: e.message }
|
|
@@ -112,7 +112,7 @@ module Legion
|
|
|
112
112
|
|
|
113
113
|
def prominent_themes(engine: nil, **)
|
|
114
114
|
themes = resolve_engine(engine).prominent_themes
|
|
115
|
-
|
|
115
|
+
log.debug "[narrative_identity] prominent_themes count=#{themes.size}"
|
|
116
116
|
{ success: true, themes: themes.map(&:to_h) }
|
|
117
117
|
rescue StandardError => e
|
|
118
118
|
{ success: false, error: e.message }
|
|
@@ -120,7 +120,7 @@ module Legion
|
|
|
120
120
|
|
|
121
121
|
def current_chapter(engine: nil, **)
|
|
122
122
|
chapter = resolve_engine(engine).current_chapter
|
|
123
|
-
|
|
123
|
+
log.debug "[narrative_identity] current_chapter=#{chapter&.id&.slice(0..7)}"
|
|
124
124
|
{ success: true, chapter: chapter&.to_h }
|
|
125
125
|
rescue StandardError => e
|
|
126
126
|
{ success: false, error: e.message }
|
|
@@ -128,7 +128,7 @@ module Legion
|
|
|
128
128
|
|
|
129
129
|
def decay_themes(engine: nil, **)
|
|
130
130
|
resolve_engine(engine).decay_all_themes!
|
|
131
|
-
|
|
131
|
+
log.debug '[narrative_identity] theme decay applied'
|
|
132
132
|
{ success: true }
|
|
133
133
|
rescue StandardError => e
|
|
134
134
|
{ success: false, error: e.message }
|
|
@@ -136,7 +136,7 @@ module Legion
|
|
|
136
136
|
|
|
137
137
|
def narrative_report(engine: nil, **)
|
|
138
138
|
report = resolve_engine(engine).narrative_report
|
|
139
|
-
|
|
139
|
+
log.debug '[narrative_identity] narrative_report generated'
|
|
140
140
|
{ success: true, report: report }
|
|
141
141
|
rescue StandardError => e
|
|
142
142
|
{ success: false, error: e.message }
|
|
@@ -20,7 +20,7 @@ module Legion
|
|
|
20
20
|
emotional_valence: emotional_valence,
|
|
21
21
|
tags: tags
|
|
22
22
|
)
|
|
23
|
-
|
|
23
|
+
log.debug "[narrative_self] recorded episode=#{episode_type} domain=#{domain} sig=#{episode.significance.round(3)}"
|
|
24
24
|
{ success: true, episode: episode.to_h }
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -41,7 +41,7 @@ module Legion
|
|
|
41
41
|
|
|
42
42
|
def create_thread(theme:, domain: :general, **)
|
|
43
43
|
thread = autobiography.create_thread(theme: theme, domain: domain)
|
|
44
|
-
|
|
44
|
+
log.debug "[narrative_self] created thread=#{theme} domain=#{domain}"
|
|
45
45
|
{ success: true, thread: thread.to_h }
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -58,13 +58,13 @@ module Legion
|
|
|
58
58
|
|
|
59
59
|
def self_summary(**)
|
|
60
60
|
summary = autobiography.self_summary
|
|
61
|
-
|
|
61
|
+
log.debug "[narrative_self] summary: episodes=#{summary[:total_episodes]} richness=#{summary[:narrative_richness].round(3)}"
|
|
62
62
|
{ success: true, summary: summary }
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def update_narrative_self(**)
|
|
66
66
|
autobiography.decay_all
|
|
67
|
-
|
|
67
|
+
log.debug "[narrative_self] tick: episodes=#{autobiography.episodes.size} threads=#{autobiography.threads.size}"
|
|
68
68
|
{ success: true, episode_count: autobiography.episodes.size, thread_count: autobiography.threads.size }
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -34,6 +34,32 @@ module Legion
|
|
|
34
34
|
false
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def pipeline_available?
|
|
38
|
+
!!(defined?(Legion::LLM::Pipeline::GaiaCaller) &&
|
|
39
|
+
Legion::LLM.respond_to?(:pipeline_enabled?) &&
|
|
40
|
+
Legion::LLM.pipeline_enabled?)
|
|
41
|
+
rescue StandardError
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def enhance(prompt, phase: 'reflection', **)
|
|
46
|
+
return nil unless available?
|
|
47
|
+
|
|
48
|
+
if pipeline_available?
|
|
49
|
+
response = Legion::LLM::Pipeline::GaiaCaller.chat(
|
|
50
|
+
message: prompt, phase: phase, **
|
|
51
|
+
)
|
|
52
|
+
response&.message&.dig(:content)
|
|
53
|
+
else
|
|
54
|
+
chat = Legion::LLM.chat
|
|
55
|
+
response = chat.ask(prompt)
|
|
56
|
+
response&.content
|
|
57
|
+
end
|
|
58
|
+
rescue StandardError => e
|
|
59
|
+
Legion::Logging.warn("[reflection:llm] enhance failed: #{e.message}")
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
|
|
37
63
|
def enhance_reflection(monitors_data:, health_scores:)
|
|
38
64
|
prompt = build_enhance_reflection_prompt(monitors_data: monitors_data, health_scores: health_scores)
|
|
39
65
|
response = llm_ask(prompt)
|
|
@@ -37,7 +37,7 @@ module Legion
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
log.debug "[reflection] generated #{new_reflections.size} reflections, health=#{reflection_store.cognitive_health}"
|
|
41
41
|
|
|
42
42
|
{
|
|
43
43
|
reflections_generated: new_reflections.size,
|
|
@@ -61,13 +61,13 @@ module Legion
|
|
|
61
61
|
|
|
62
62
|
reflection ||= build_mechanical_dream_reflection(dream_results)
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
log.debug "[reflection] dream reflection generated source=#{source}"
|
|
65
65
|
{ reflection: reflection, source: source }
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def cognitive_health(**)
|
|
69
69
|
health = reflection_store.cognitive_health
|
|
70
|
-
|
|
70
|
+
log.debug "[reflection] cognitive health: #{health}"
|
|
71
71
|
{
|
|
72
72
|
health: health,
|
|
73
73
|
category_scores: Helpers::Constants::CATEGORIES.to_h { |c| [c, reflection_store.category_score(c)] },
|
|
@@ -94,7 +94,7 @@ module Legion
|
|
|
94
94
|
return { error: :already_acted } if reflection[:acted_on]
|
|
95
95
|
|
|
96
96
|
reflection_store.mark_acted_on(reflection_id)
|
|
97
|
-
|
|
97
|
+
log.info "[reflection] adapted: #{reflection[:observation]}"
|
|
98
98
|
{ adapted: true, reflection_id: reflection_id, recommendation: reflection[:recommendation] }
|
|
99
99
|
end
|
|
100
100
|
|
|
@@ -18,13 +18,13 @@ module Legion
|
|
|
18
18
|
volume: volume,
|
|
19
19
|
bias_direction: bias_direction
|
|
20
20
|
)
|
|
21
|
-
|
|
21
|
+
log.info "[self_talk] register_voice: name=#{name} type=#{voice_type} registered=#{result[:registered]}"
|
|
22
22
|
result
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def start_dialogue(topic:, **)
|
|
26
26
|
result = engine.start_dialogue(topic: topic)
|
|
27
|
-
|
|
27
|
+
log.debug "[self_talk] start_dialogue: topic=#{topic} id=#{result[:dialogue][:id]}"
|
|
28
28
|
result
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -36,14 +36,14 @@ module Legion
|
|
|
36
36
|
position: position,
|
|
37
37
|
strength: strength
|
|
38
38
|
)
|
|
39
|
-
|
|
39
|
+
log.debug "[self_talk] add_turn: dialogue=#{dialogue_id} voice=#{voice_id} added=#{result[:added]}"
|
|
40
40
|
result
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def conclude_dialogue(dialogue_id:, summary: nil, **)
|
|
44
44
|
resolved_summary = summary || generate_summary_for_dialogue(dialogue_id)
|
|
45
45
|
result = engine.conclude_dialogue(dialogue_id: dialogue_id, summary: resolved_summary)
|
|
46
|
-
|
|
46
|
+
log.info "[self_talk] conclude_dialogue: id=#{dialogue_id} concluded=#{result[:concluded]}"
|
|
47
47
|
result
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -59,37 +59,37 @@ module Legion
|
|
|
59
59
|
content: content[:content],
|
|
60
60
|
position: content[:position]
|
|
61
61
|
)
|
|
62
|
-
|
|
62
|
+
log.debug "[self_talk] generate_voice_turn: dialogue=#{dialogue_id} voice=#{voice_id} source=#{source}"
|
|
63
63
|
{ generated: true, source: source, turn: turn_result[:turn] }
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def deadlock_dialogue(dialogue_id:, **)
|
|
67
67
|
result = engine.deadlock_dialogue(dialogue_id: dialogue_id)
|
|
68
|
-
|
|
68
|
+
log.warn "[self_talk] deadlock_dialogue: id=#{dialogue_id} deadlocked=#{result[:deadlocked]}"
|
|
69
69
|
result
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def amplify_voice(voice_id:, amount: Helpers::Constants::VOLUME_BOOST, **)
|
|
73
73
|
result = engine.amplify_voice(voice_id: voice_id, amount: amount)
|
|
74
|
-
|
|
74
|
+
log.debug "[self_talk] amplify_voice: id=#{voice_id} volume=#{result[:volume]}"
|
|
75
75
|
result
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def dampen_voice(voice_id:, amount: Helpers::Constants::VOLUME_DECAY, **)
|
|
79
79
|
result = engine.dampen_voice(voice_id: voice_id, amount: amount)
|
|
80
|
-
|
|
80
|
+
log.debug "[self_talk] dampen_voice: id=#{voice_id} volume=#{result[:volume]}"
|
|
81
81
|
result
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
def dialogue_report(dialogue_id:, **)
|
|
85
85
|
result = engine.dialogue_report(dialogue_id: dialogue_id)
|
|
86
|
-
|
|
86
|
+
log.debug "[self_talk] dialogue_report: id=#{dialogue_id} found=#{result[:found]}"
|
|
87
87
|
result
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def self_talk_status(**)
|
|
91
91
|
summary = engine.to_h
|
|
92
|
-
|
|
92
|
+
log.debug "[self_talk] status: voices=#{summary[:voice_count]} dialogues=#{summary[:dialogue_count]}"
|
|
93
93
|
summary
|
|
94
94
|
end
|
|
95
95
|
|
|
@@ -100,7 +100,7 @@ module Legion
|
|
|
100
100
|
decayed += 1
|
|
101
101
|
{ id: voice.id, name: voice.name, volume: voice.volume }
|
|
102
102
|
end
|
|
103
|
-
|
|
103
|
+
log.debug "[self-talk] voice decay: decayed=#{decayed} voices"
|
|
104
104
|
{ decayed: decayed, voices: voice_list }
|
|
105
105
|
end
|
|
106
106
|
|
|
@@ -1,6 +1,82 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
RSpec.describe Legion::Extensions::Agentic::Self::Reflection::Helpers::LlmEnhancer do
|
|
4
|
+
let(:enhancer_mod) { described_class }
|
|
5
|
+
|
|
6
|
+
describe '.pipeline_available?' do
|
|
7
|
+
it 'returns false when GaiaCaller is not defined' do
|
|
8
|
+
begin
|
|
9
|
+
hide_const('Legion::LLM::Pipeline::GaiaCaller')
|
|
10
|
+
rescue StandardError
|
|
11
|
+
nil
|
|
12
|
+
end
|
|
13
|
+
expect(described_class.pipeline_available?).to be false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'returns false when pipeline_enabled? is false' do
|
|
17
|
+
stub_const('Legion::LLM::Pipeline::GaiaCaller', double)
|
|
18
|
+
stub_const('Legion::LLM', double(respond_to?: true, pipeline_enabled?: false))
|
|
19
|
+
expect(described_class.pipeline_available?).to be false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'returns true when GaiaCaller defined and pipeline enabled' do
|
|
23
|
+
gaia_caller_mod = Module.new
|
|
24
|
+
pipeline_mod = Module.new
|
|
25
|
+
pipeline_mod.const_set(:GaiaCaller, gaia_caller_mod)
|
|
26
|
+
llm_mod = Module.new
|
|
27
|
+
llm_mod.const_set(:Pipeline, pipeline_mod)
|
|
28
|
+
llm_mod.define_singleton_method(:respond_to?) { |*| true }
|
|
29
|
+
llm_mod.define_singleton_method(:pipeline_enabled?) { true }
|
|
30
|
+
stub_const('Legion::LLM', llm_mod)
|
|
31
|
+
expect(described_class.pipeline_available?).to be true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'returns false on any error' do
|
|
35
|
+
stub_const('Legion::LLM', double)
|
|
36
|
+
allow(Legion::LLM).to receive(:respond_to?).and_raise(StandardError)
|
|
37
|
+
expect(described_class.pipeline_available?).to be false
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '.enhance' do
|
|
42
|
+
it 'uses GaiaCaller when pipeline is available' do
|
|
43
|
+
allow(enhancer_mod).to receive(:available?).and_return(true)
|
|
44
|
+
allow(enhancer_mod).to receive(:pipeline_available?).and_return(true)
|
|
45
|
+
|
|
46
|
+
mock_response = double(message: { content: 'reflected' })
|
|
47
|
+
gaia_caller_mod = Module.new
|
|
48
|
+
gaia_caller_mod.define_singleton_method(:chat) { |**| mock_response }
|
|
49
|
+
pipeline_mod = Module.new
|
|
50
|
+
pipeline_mod.const_set(:GaiaCaller, gaia_caller_mod)
|
|
51
|
+
llm_mod = Module.new
|
|
52
|
+
llm_mod.const_set(:Pipeline, pipeline_mod)
|
|
53
|
+
stub_const('Legion::LLM', llm_mod)
|
|
54
|
+
|
|
55
|
+
expect(gaia_caller_mod).to receive(:chat)
|
|
56
|
+
.with(hash_including(phase: 'reflection'))
|
|
57
|
+
.and_return(mock_response)
|
|
58
|
+
|
|
59
|
+
result = enhancer_mod.enhance('reflect on this', phase: 'reflection')
|
|
60
|
+
expect(result).to eq('reflected')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'falls back to legacy chat when pipeline unavailable' do
|
|
64
|
+
allow(enhancer_mod).to receive(:available?).and_return(true)
|
|
65
|
+
allow(enhancer_mod).to receive(:pipeline_available?).and_return(false)
|
|
66
|
+
|
|
67
|
+
mock_chat = double(ask: double(content: 'legacy reflected'))
|
|
68
|
+
stub_const('Legion::LLM', double(chat: mock_chat))
|
|
69
|
+
|
|
70
|
+
result = enhancer_mod.enhance('reflect on this', phase: 'reflection')
|
|
71
|
+
expect(result).to eq('legacy reflected')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'returns nil when not available' do
|
|
75
|
+
allow(enhancer_mod).to receive(:available?).and_return(false)
|
|
76
|
+
expect(enhancer_mod.enhance('hello')).to be_nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
4
80
|
describe '.available?' do
|
|
5
81
|
context 'when Legion::LLM is not defined' do
|
|
6
82
|
it 'returns a falsy value' do
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'bundler/setup'
|
|
4
|
+
require 'legion/logging'
|
|
5
|
+
require 'legion/settings'
|
|
6
|
+
require 'legion/cache/helper'
|
|
7
|
+
require 'legion/crypt/helper'
|
|
8
|
+
require 'legion/data/helper'
|
|
9
|
+
require 'legion/json/helper'
|
|
10
|
+
require 'legion/transport/helper'
|
|
4
11
|
|
|
5
12
|
module Legion
|
|
6
|
-
module Logging
|
|
7
|
-
def self.debug(_msg); end
|
|
8
|
-
def self.info(_msg); end
|
|
9
|
-
def self.warn(_msg); end
|
|
10
|
-
def self.error(_msg); end
|
|
11
|
-
def self.fatal(_msg); end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
13
|
module Extensions
|
|
15
14
|
module Core
|
|
16
15
|
def self.extended(_base); end
|
|
@@ -18,24 +17,30 @@ module Legion
|
|
|
18
17
|
|
|
19
18
|
module Helpers
|
|
20
19
|
module Lex
|
|
21
|
-
|
|
20
|
+
include Legion::Logging::Helper
|
|
21
|
+
include Legion::Settings::Helper
|
|
22
|
+
include Legion::Cache::Helper
|
|
23
|
+
include Legion::Crypt::Helper
|
|
24
|
+
include Legion::Data::Helper
|
|
25
|
+
include Legion::JSON::Helper
|
|
26
|
+
include Legion::Transport::Helper
|
|
22
27
|
end
|
|
23
28
|
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
29
|
|
|
27
|
-
# rubocop:disable Lint/EmptyClass, Style/OneClassPerFile
|
|
28
|
-
module Legion
|
|
29
|
-
module Extensions
|
|
30
30
|
module Actors
|
|
31
|
-
class Every
|
|
32
|
-
|
|
31
|
+
class Every
|
|
32
|
+
include Helpers::Lex
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Once
|
|
36
|
+
include Helpers::Lex
|
|
37
|
+
end
|
|
33
38
|
end
|
|
34
39
|
end
|
|
35
40
|
end
|
|
41
|
+
|
|
36
42
|
$LOADED_FEATURES << 'legion/extensions/actors/every'
|
|
37
43
|
$LOADED_FEATURES << 'legion/extensions/actors/once'
|
|
38
|
-
# rubocop:enable Lint/EmptyClass, Style/OneClassPerFile
|
|
39
44
|
|
|
40
45
|
require 'legion/extensions/agentic/self'
|
|
41
46
|
|