Class: DecisionAgent::Dmn::DecisionGraph

Inherits:
Object
  • Object
show all
Defined in:
lib/decision_agent/dmn/decision_graph.rb

Overview

Represents and evaluates a decision graph (DMN model with multiple decisions)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id:, name:) ⇒ DecisionGraph

Returns a new instance of DecisionGraph.



66
67
68
69
70
71
# File 'lib/decision_agent/dmn/decision_graph.rb', line 66

def initialize(id:, name:)
  @id = id
  @name = name
  @decisions = {} # decision_id => DecisionNode
  @feel_evaluator = Feel::Evaluator.new
end

Instance Attribute Details

#decisionsObject (readonly)

Returns the value of attribute decisions.



64
65
66
# File 'lib/decision_agent/dmn/decision_graph.rb', line 64

def decisions
  @decisions
end

#idObject (readonly)

Returns the value of attribute id.



64
65
66
# File 'lib/decision_agent/dmn/decision_graph.rb', line 64

def id
  @id
end

#nameObject (readonly)

Returns the value of attribute name.



64
65
66
# File 'lib/decision_agent/dmn/decision_graph.rb', line 64

def name
  @name
end

Instance Method Details

#add_decision(decision) ⇒ Object



73
74
75
# File 'lib/decision_agent/dmn/decision_graph.rb', line 73

def add_decision(decision)
  @decisions[decision.id] = decision
end

#circular_dependencies?Boolean

Detect circular dependencies

Returns:



137
138
139
140
141
142
# File 'lib/decision_agent/dmn/decision_graph.rb', line 137

def circular_dependencies?
  topological_order
  false
rescue DmnError => e
  e.message.include?("Circular dependency")
end

#dependency_graphObject

Get the dependency graph as a hash



162
163
164
165
166
167
168
# File 'lib/decision_agent/dmn/decision_graph.rb', line 162

def dependency_graph
  graph = {}
  @decisions.each do |id, decision|
    graph[id] = decision.information_requirements.map { |req| req[:decision_id] }
  end
  graph
end

#evaluate(decision_id, context) ⇒ Object

Evaluate a specific decision (and all its dependencies)

Raises:



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/decision_agent/dmn/decision_graph.rb', line 82

def evaluate(decision_id, context)
  decision = @decisions[decision_id]
  raise DmnError, "Decision '#{decision_id}' not found" unless decision

  # Reset all decision evaluations
  reset_all!

  # Build evaluation context
  eval_context = context.is_a?(Hash) ? context : context.to_h

  # Evaluate the requested decision (will recursively evaluate dependencies)
  evaluate_decision(decision, eval_context)
end

#evaluate_all(context) ⇒ Object

Evaluate all decisions in the graph



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/decision_agent/dmn/decision_graph.rb', line 97

def evaluate_all(context)
  reset_all!
  eval_context = context.is_a?(Hash) ? context : context.to_h

  results = {}
  @decisions.each do |decision_id, decision|
    results[decision_id] = evaluate_decision(decision, eval_context) unless decision.evaluated
  end

  results
end

#get_decision(decision_id) ⇒ Object



77
78
79
# File 'lib/decision_agent/dmn/decision_graph.rb', line 77

def get_decision(decision_id)
  @decisions[decision_id]
end

#leaf_decisionsObject

Get all leaf decisions (no other decisions depend on them)



145
146
147
148
149
150
151
152
153
154
# File 'lib/decision_agent/dmn/decision_graph.rb', line 145

def leaf_decisions
  dependent_decisions = Set.new
  @decisions.each_value do |decision|
    decision.information_requirements.each do |req|
      dependent_decisions.add(req[:decision_id])
    end
  end

  @decisions.keys.reject { |id| dependent_decisions.include?(id) }
end

#root_decisionsObject

Get all root decisions (don't depend on other decisions)



157
158
159
# File 'lib/decision_agent/dmn/decision_graph.rb', line 157

def root_decisions
  @decisions.select { |_id, decision| decision.information_requirements.empty? }.keys
end

#to_hObject

Export graph structure



171
172
173
174
175
176
177
178
# File 'lib/decision_agent/dmn/decision_graph.rb', line 171

def to_h
  {
    id: @id,
    name: @name,
    decisions: @decisions.transform_values(&:to_h),
    dependency_graph: dependency_graph
  }
end

#topological_orderObject

Get decisions in topological order (respecting dependencies)



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/decision_agent/dmn/decision_graph.rb', line 110

def topological_order
  order = []
  visited = Set.new
  temp_mark = Set.new

  visit = lambda do |decision_id|
    return if visited.include?(decision_id)

    raise DmnError, "Circular dependency detected involving decision '#{decision_id}'" if temp_mark.include?(decision_id)

    temp_mark.add(decision_id)

    decision = @decisions[decision_id]
    decision.information_requirements.each do |req|
      visit.call(req[:decision_id]) if @decisions[req[:decision_id]]
    end

    temp_mark.delete(decision_id)
    visited.add(decision_id)
    order << decision_id
  end

  @decisions.each_key { |decision_id| visit.call(decision_id) }
  order
end