Class: Kernai::Plan

Inherits:
Object
  • Object
show all
Defined in:
lib/kernai/plan.rb

Overview

Structured workflow plan emitted by the LLM inside a <block type=“plan”>. A plan is a declarative description of tasks to run; the Kernel executes it via the TaskScheduler and sub-agents.

Defined Under Namespace

Classes: Result

Constant Summary collapse

VALID_STRATEGIES =
%w[parallel sequential mixed].freeze
DEFAULT_STRATEGY =

Default honors each task’s own ‘parallel` flag. The LLM can override with “parallel” (run everything concurrently) or “sequential” (force serial execution) as a global hint to the scheduler.

'mixed'
REJECTION_REASONS =

Symbols returned by Plan.validate when a raw payload is rejected.

%i[
  blank
  invalid_json
  not_a_hash
  no_tasks
  empty_tasks
  all_tasks_invalid
  cyclic
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(goal:, strategy:, tasks:) ⇒ Plan

Returns a new instance of Plan.



111
112
113
114
115
# File 'lib/kernai/plan.rb', line 111

def initialize(goal:, strategy:, tasks:)
  @goal = goal
  @strategy = strategy
  @tasks = tasks
end

Instance Attribute Details

#goalObject (readonly)

Returns the value of attribute goal.



35
36
37
# File 'lib/kernai/plan.rb', line 35

def goal
  @goal
end

#strategyObject (readonly)

Returns the value of attribute strategy.



35
36
37
# File 'lib/kernai/plan.rb', line 35

def strategy
  @strategy
end

#tasksObject (readonly)

Returns the value of attribute tasks.



35
36
37
# File 'lib/kernai/plan.rb', line 35

def tasks
  @tasks
end

Class Method Details

.parse(raw) ⇒ Object

Convenience wrapper — returns the Plan or nil. Use ‘validate` when the caller also needs the rejection reason.



40
41
42
# File 'lib/kernai/plan.rb', line 40

def parse(raw)
  validate(raw).plan
end

.validate(raw) ⇒ Object

Full parsing pipeline with structured failure reasons. Always returns a Result; the caller decides whether to surface ‘reason`.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/kernai/plan.rb', line 46

def validate(raw)
  data, reason = coerce(raw)
  return Result.new(reason: reason) if reason

  tasks_data = data['tasks']
  return Result.new(reason: :no_tasks)    unless tasks_data.is_a?(Array)
  return Result.new(reason: :empty_tasks) if tasks_data.empty?

  tasks = tasks_data.filter_map { |t| Task.from_hash(t) }
  return Result.new(reason: :all_tasks_invalid) if tasks.empty?

  prune_invalid_dependencies(tasks)
  return Result.new(reason: :cyclic) if cyclic?(tasks)

  plan = new(
    goal: data['goal'].to_s,
    strategy: VALID_STRATEGIES.include?(data['strategy']) ? data['strategy'] : DEFAULT_STRATEGY,
    tasks: tasks
  )
  Result.new(plan: plan)
end

Instance Method Details

#to_hObject



117
118
119
120
121
122
123
# File 'lib/kernai/plan.rb', line 117

def to_h
  {
    goal: @goal,
    strategy: @strategy,
    tasks: @tasks.map(&:to_h)
  }
end