Class: Legion::Extensions::Agentic::Executive::Planning::Helpers::PlanStore

Inherits:
Object
  • Object
show all
Defined in:
lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePlanStore

Returns a new instance of PlanStore.



12
13
14
15
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 12

def initialize
  @plans        = {}
  @plan_history = []
end

Instance Attribute Details

#plan_historyObject (readonly)

Returns the value of attribute plan_history.



10
11
12
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 10

def plan_history
  @plan_history
end

#plansObject (readonly)

Returns the value of attribute plans.



10
11
12
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 10

def plans
  @plans
end

Instance Method Details

#abandon_plan(plan_id:, reason: nil) ⇒ Object



79
80
81
82
83
84
85
86
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 79

def abandon_plan(plan_id:, reason: nil)
  plan = @plans[plan_id]
  return { error: 'plan not found' } unless plan

  plan.status = :abandoned
  archive_plan(plan_id)
  { success: true, plan_id: plan_id, reason: reason }
end

#active_plansObject



88
89
90
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 88

def active_plans
  @plans.values.select { |p| %i[active executing].include?(p.status) }
end

#advance_step(plan_id:, step_id:, result: {}) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 38

def advance_step(plan_id:, step_id:, result: {})
  plan = @plans[plan_id]
  return { error: 'plan not found' } unless plan

  step = plan.advance!(step_id, result: result)
  return { error: 'step not found' } unless step

  if plan.complete?
    plan.status = :completed
    archive_plan(plan_id)
  end

  { success: true, step_id: step_id, plan_progress: plan.progress.round(4), plan_status: plan.status }
end

#completed_plans(limit: 10) ⇒ Object



92
93
94
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 92

def completed_plans(limit: 10)
  @plan_history.last(limit)
end

#create_plan(goal:, steps: [], priority: :medium, contingencies: {}, parent_plan_id: nil) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 17

def create_plan(goal:, steps: [], priority: :medium, contingencies: {}, parent_plan_id: nil, **)
  step_objects = steps.map do |s|
    s.is_a?(PlanStep) ? s : PlanStep.new(**s)
  end
  plan = Plan.new(
    goal:           goal,
    steps:          step_objects,
    priority:       priority,
    contingencies:  contingencies,
    parent_plan_id: parent_plan_id
  )
  plan.status = :active
  @plans[plan.id] = plan
  trim_history
  plan
end

#fail_step(plan_id:, step_id:, reason: nil) ⇒ Object



53
54
55
56
57
58
59
60
61
62
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 53

def fail_step(plan_id:, step_id:, reason: nil)
  plan = @plans[plan_id]
  return { error: 'plan not found' } unless plan

  step = plan.fail_step!(step_id, reason: reason)
  return { error: 'step not found' } unless step

  contingency = plan.contingencies[step.action] || plan.contingencies[step_id]
  { success: true, step_id: step_id, contingency: contingency, plan_status: plan.status }
end

#find_plan(plan_id) ⇒ Object



34
35
36
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 34

def find_plan(plan_id)
  @plans[plan_id]
end

#plan_progress(plan_id) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 96

def plan_progress(plan_id)
  plan = @plans[plan_id] || @plan_history.find { |p| p.id == plan_id }
  return { error: 'plan not found' } unless plan

  ready_ids = plan.completed_step_ids
  {
    plan_id:      plan_id,
    goal:         plan.goal,
    status:       plan.status,
    progress:     plan.progress.round(4),
    total_steps:  plan.steps.size,
    completed:    plan.steps.count { |s| s.status == :completed },
    failed:       plan.steps.count { |s| s.status == :failed },
    pending:      plan.steps.count { |s| s.status == :pending },
    blocked:      plan.steps.count { |s| s.status == :blocked },
    next_ready:   plan.steps.select { |s| s.status == :pending && s.ready?(ready_ids) }.map(&:id),
    replan_count: plan.replan_count,
    stale:        plan.stale?
  }
end

#plans_by_priorityObject



117
118
119
120
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 117

def plans_by_priority
  priority_value = ->(p) { Constants::PRIORITIES.fetch(p.priority, 0.5) }
  @plans.values.sort_by { |p| -priority_value.call(p) }
end

#replan(plan_id:, new_steps:, reason: nil) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 64

def replan(plan_id:, new_steps:, reason: nil)
  plan = @plans[plan_id]
  return { error: 'plan not found' } unless plan
  return { error: 'replan limit reached' } if plan.replan_count >= Constants::REPLAN_LIMIT

  step_objects = new_steps.map do |s|
    s.is_a?(PlanStep) ? s : PlanStep.new(**s)
  end
  plan.increment_replan!
  plan.replace_remaining_steps!(step_objects)
  plan.status = :active

  { success: true, plan_id: plan_id, replan_count: plan.replan_count, reason: reason }
end

#statsObject



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 122

def stats
  all = @plans.values
  total = all.size + @plan_history.size
  by_status = Constants::PLAN_STATUSES.to_h { |s| [s, all.count { |p| p.status == s }] }
  replanned = all.count { |p| p.replan_count.positive? }
  avg_progress = all.empty? ? 0.0 : (all.sum(&:progress) / all.size).round(4)
  replan_rate = total.zero? ? 0.0 : (replanned.to_f / [all.size, 1].max).round(4)

  {
    active_plan_count:   all.size,
    archived_plan_count: @plan_history.size,
    by_status:           by_status,
    avg_progress:        avg_progress,
    replan_rate:         replan_rate
  }
end

#trim_historyObject



139
140
141
142
143
144
# File 'lib/legion/extensions/agentic/executive/planning/helpers/plan_store.rb', line 139

def trim_history
  return unless @plans.size > Constants::MAX_PLANS

  oldest_id = @plans.keys.min_by { |k| @plans[k].created_at }
  archive_plan(oldest_id)
end