Class: Legion::Extensions::Agentic::Executive::GoalManagement::Helpers::GoalEngine
- Inherits:
-
Object
- Object
- Legion::Extensions::Agentic::Executive::GoalManagement::Helpers::GoalEngine
show all
- Includes:
- Constants
- Defined in:
- lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb
Constant Summary
collapse
- URGENCY_BOOST =
{ critical: 0.3, high: 0.2, moderate: 0.1, low: 0.05 }.freeze
Constants included
from Constants
Constants::CONFLICT_LABELS, Constants::CONFLICT_THRESHOLD, Constants::DEFAULT_PRIORITY, Constants::GOAL_STATUSES, Constants::MAX_DEPTH, Constants::MAX_GOALS, Constants::PRIORITY_BOOST, Constants::PRIORITY_DECAY, Constants::PRIORITY_LABELS, Constants::PROGRESS_LABELS, Constants::PROGRESS_THRESHOLD
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#abandon_goal(goal_id:) ⇒ Object
-
#activate_goal(goal_id:) ⇒ Object
-
#active_goals ⇒ Object
-
#add_goal(content:, parent_id: nil, domain: :general, priority: DEFAULT_PRIORITY, deadline: nil) ⇒ Object
-
#advance_progress(goal_id:, amount:) ⇒ Object
-
#assign_task_to_goal(goal_id:, task_id:, runner_mapping:) ⇒ Object
-
#block_goal(goal_id:) ⇒ Object
-
#blocked_goals ⇒ Object
-
#complete_goal(goal_id:) ⇒ Object
-
#completed_goals ⇒ Object
-
#decay_all_priorities! ⇒ Object
-
#decompose(goal_id:, sub_goals:) ⇒ Object
-
#detect_conflicts(goal_id:) ⇒ Object
-
#goal_report ⇒ Object
-
#goal_tree(goal_id:) ⇒ Object
-
#highest_priority(limit: 5) ⇒ Object
-
#initialize ⇒ GoalEngine
constructor
A new instance of GoalEngine.
-
#overdue_goals ⇒ Object
-
#reprioritize!(signal:) ⇒ Object
-
#to_h ⇒ Object
-
#unblock_goal(goal_id:) ⇒ Object
-
#update_from_task_event(task_id:, status:, result: nil) ⇒ Object
Constructor Details
Returns a new instance of GoalEngine.
16
17
18
19
20
21
22
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 16
def initialize
@goals = {}
@root_goal_ids = []
@mutex = Mutex.new
@persistence = GoalPersistence.new
rehydrate_from_cache
end
|
Instance Attribute Details
#goals ⇒ Object
Returns the value of attribute goals.
14
15
16
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 14
def goals
@goals
end
|
#root_goal_ids ⇒ Object
Returns the value of attribute root_goal_ids.
14
15
16
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 14
def root_goal_ids
@root_goal_ids
end
|
Instance Method Details
#abandon_goal(goal_id:) ⇒ Object
88
89
90
91
92
93
94
95
96
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 88
def abandon_goal(goal_id:)
goal = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless goal
abandoned = goal.abandon!
persist_goal(goal) if abandoned
log.debug "[goal_management] abandon goal=#{goal_id} result=#{abandoned}"
{ success: abandoned, goal_id: goal_id, status: goal.status }
end
|
#activate_goal(goal_id:) ⇒ Object
68
69
70
71
72
73
74
75
76
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 68
def activate_goal(goal_id:)
goal = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless goal
activated = goal.activate!
persist_goal(goal) if activated
log.debug "[goal_management] activate goal=#{goal_id} result=#{activated}"
{ success: activated, goal_id: goal_id, status: goal.status }
end
|
#active_goals ⇒ Object
150
151
152
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 150
def active_goals
@goals.values.select(&:active?)
end
|
#add_goal(content:, parent_id: nil, domain: :general, priority: DEFAULT_PRIORITY, deadline: nil) ⇒ Object
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 24
def add_goal(content:, parent_id: nil, domain: :general, priority: DEFAULT_PRIORITY, deadline: nil)
return { success: false, error: 'goal limit reached' } if @goals.size >= MAX_GOALS
if parent_id
parent = @goals[parent_id]
return { success: false, error: "parent goal #{parent_id} not found" } unless parent
return { success: false, error: 'max tree depth exceeded' } if depth_of(parent_id) >= MAX_DEPTH
end
prune_if_needed
goal = Goal.new(content: content, parent_id: parent_id, domain: domain,
priority: priority, deadline: deadline)
@goals[goal.id] = goal
if parent_id
@goals[parent_id].add_sub_goal(goal.id)
else
@root_goal_ids << goal.id
end
persist_goal(goal)
log.debug "[goal_management] add_goal id=#{goal.id} domain=#{domain} priority=#{priority.round(2)}"
{ success: true, goal: goal.to_h }
end
|
#advance_progress(goal_id:, amount:) ⇒ Object
118
119
120
121
122
123
124
125
126
127
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 118
def advance_progress(goal_id:, amount:)
goal = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless goal
goal.advance_progress!(amount)
propagate_progress_to_parent(goal_id)
persist_goal(goal)
log.debug "[goal_management] advance_progress goal=#{goal_id} progress=#{goal.progress.round(2)}"
{ success: true, goal_id: goal_id, progress: goal.progress }
end
|
#assign_task_to_goal(goal_id:, task_id:, runner_mapping:) ⇒ Object
198
199
200
201
202
203
204
205
206
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 198
def assign_task_to_goal(goal_id:, task_id:, runner_mapping:)
goal = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless goal
goal.assign_task!(task_id: task_id, runner_mapping: runner_mapping)
persist_goal(goal)
log.debug "[goal_management] assign_task goal=#{goal_id} task_id=#{task_id}"
{ success: true, goal_id: goal_id, task_id: task_id }
end
|
#block_goal(goal_id:) ⇒ Object
98
99
100
101
102
103
104
105
106
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 98
def block_goal(goal_id:)
goal = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless goal
blocked = goal.block!
persist_goal(goal) if blocked
log.debug "[goal_management] block goal=#{goal_id} result=#{blocked}"
{ success: blocked, goal_id: goal_id, status: goal.status }
end
|
#blocked_goals ⇒ Object
154
155
156
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 154
def blocked_goals
@goals.values.select(&:blocked?)
end
|
#complete_goal(goal_id:) ⇒ Object
78
79
80
81
82
83
84
85
86
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 78
def complete_goal(goal_id:)
goal = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless goal
completed = goal.complete!
persist_goal(goal) if completed
log.debug "[goal_management] complete goal=#{goal_id} result=#{completed}"
{ success: completed, goal_id: goal_id, status: goal.status }
end
|
#completed_goals ⇒ Object
162
163
164
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 162
def completed_goals
@goals.values.select(&:completed?)
end
|
#decay_all_priorities! ⇒ Object
208
209
210
211
212
213
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 208
def decay_all_priorities!
inactive = @goals.values.reject { |g| g.status == :active }
inactive.each(&:decay_priority!)
log.debug "[goal_management] decay_all inactive=#{inactive.size}"
{ decayed: inactive.size }
end
|
#decompose(goal_id:, sub_goals:) ⇒ Object
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 49
def decompose(goal_id:, sub_goals:)
parent = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless parent
created = sub_goals.map do |sg|
content = sg.fetch(:content, sg.fetch('content', ''))
domain = sg.fetch(:domain, sg.fetch('domain', parent.domain))
priority = sg.fetch(:priority, sg.fetch('priority', parent.priority))
deadline = sg.fetch(:deadline, sg.fetch('deadline', nil))
add_goal(content: content, parent_id: goal_id, domain: domain,
priority: priority, deadline: deadline)
end
failures = created.reject { |r| r[:success] }
log.debug "[goal_management] decompose parent=#{goal_id} created=#{created.size - failures.size} failed=#{failures.size}"
{ success: true, parent_id: goal_id, created: created, failures: failures.size }
end
|
#detect_conflicts(goal_id:) ⇒ Object
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 129
def detect_conflicts(goal_id:)
goal = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless goal
competing = @goals.values.select do |g|
g.id != goal_id &&
g.domain == goal.domain &&
%i[proposed active blocked].include?(g.status)
end
raw = competing.map do |g|
score = conflict_score(goal, g)
{ goal_id: g.id, content: g.content, conflict_score: score, label: conflict_label(score) }
end
conflicts = raw.select { |c| c[:conflict_score] > 0.0 }
.sort_by { |c| -c[:conflict_score] }
log.debug "[goal_management] detect_conflicts goal=#{goal_id} conflicts=#{conflicts.size}"
{ success: true, goal_id: goal_id, conflicts: conflicts, count: conflicts.size }
end
|
#goal_report ⇒ Object
239
240
241
242
243
244
245
246
247
248
249
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 239
def goal_report
statuses = GOAL_STATUSES.to_h { |s| [s, 0] }
@goals.each_value { |g| statuses[g.status] += 1 }
{
total: @goals.size,
root_goals: @root_goal_ids.size,
statuses: statuses,
overdue: overdue_goals.size,
high_priority: @goals.values.count { |g| g.priority >= 0.6 }
}
end
|
#goal_tree(goal_id:) ⇒ Object
166
167
168
169
170
171
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 166
def goal_tree(goal_id:)
goal = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless goal
{ success: true, tree: build_tree(goal_id) }
end
|
#highest_priority(limit: 5) ⇒ Object
173
174
175
176
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 173
def highest_priority(limit: 5)
active = @goals.values.select { |g| %i[proposed active blocked].include?(g.status) }
active.sort_by { |g| -g.priority }.first(limit)
end
|
#overdue_goals ⇒ Object
158
159
160
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 158
def overdue_goals
@goals.values.select(&:overdue?)
end
|
#reprioritize!(signal:) ⇒ Object
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 180
def reprioritize!(signal:)
domain = signal[:domain]&.to_sym
boost = URGENCY_BOOST[signal[:urgency]&.to_sym] || 0.05
adjusted = 0
@goals.each_value do |goal|
next unless goal.domain == domain && goal.active?
times = (boost / Constants::PRIORITY_BOOST).ceil
times.times { goal.boost_priority! }
persist_goal(goal)
adjusted += 1
end
log.info "[goal_engine] reprioritize! domain=#{domain} boost=#{boost} adjusted=#{adjusted}"
{ adjusted: adjusted, domain: domain, boost: boost }
end
|
#to_h ⇒ Object
251
252
253
254
255
256
257
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 251
def to_h
{
goals: @goals.transform_values(&:to_h),
root_goal_ids: @root_goal_ids.dup,
report: goal_report
}
end
|
#unblock_goal(goal_id:) ⇒ Object
108
109
110
111
112
113
114
115
116
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 108
def unblock_goal(goal_id:)
goal = @goals[goal_id]
return { success: false, error: "goal #{goal_id} not found" } unless goal
unblocked = goal.unblock!
persist_goal(goal) if unblocked
log.debug "[goal_management] unblock goal=#{goal_id} result=#{unblocked}"
{ success: unblocked, goal_id: goal_id, status: goal.status }
end
|
#update_from_task_event(task_id:, status:, result: nil) ⇒ Object
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb', line 215
def update_from_task_event(task_id:, status:, result: nil)
@mutex.synchronize do
goal = @goals.values.find { |g| g.task_id == task_id }
return { found: false } unless goal
case status
when 'task.completed'
goal.advance_progress!(1.0 - goal.progress)
goal.complete! if goal.progress >= Constants::PROGRESS_THRESHOLD
propagate_progress_to_parent(goal.id) if goal.parent_id
persist_goal(goal)
log.info "[goal_engine] goal status change goal=#{goal.id} transition=completed"
{ found: true, goal_id: goal.id, new_status: goal.status, progress: goal.progress }
when 'task.exception', 'task.failed'
goal.block!
persist_goal(goal)
log.info "[goal_engine] goal status change goal=#{goal.id} transition=blocked"
{ found: true, goal_id: goal.id, new_status: :blocked, error: result }
else
{ found: true, goal_id: goal.id, unhandled_status: status }
end
end
end
|