Class: Ask::Graph
- Inherits:
-
Object
- Object
- Ask::Graph
- Defined in:
- lib/ask/graph.rb,
lib/ask/graph/runner.rb,
lib/ask/graph/context.rb,
lib/ask/graph/version.rb
Overview
Define durable workflow graphs with named steps, conditional routing, parallel execution, sub-graph composition, human-in-the-loop approval, per-item checkpointing loops, step timeouts, retry policies, and lifecycle hooks.
Defined Under Namespace
Classes: Context, Paused, Runner, StepFailed, StepTimeout
Constant Summary collapse
- VERSION =
"0.6.1"
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
-
#runner ⇒ Object
readonly
Returns the value of attribute runner.
Class Method Summary collapse
-
.after_step(method_name) ⇒ Object
Register a callback to run after every successful step.
-
.approve(klass, **opts) ⇒ Object
Declare a human-in-the-loop step.
-
.before_step(method_name) ⇒ Object
Register a callback to run before every step.
-
.call(input = nil, storage: nil) ⇒ Object
Create an instance and run it.
-
.declarations ⇒ Object
All step declarations collected across the class hierarchy.
-
.inherited(subclass) ⇒ Object
Inherited hook — copies declarations to subclasses.
-
.lifecycle_hooks ⇒ Object
--- Lifecycle hooks ---.
-
.on_failure(method_name) ⇒ Object
Register a callback to run when a step fails.
-
.run ⇒ Object
Create an instance and run it.
-
.step(klass, **opts) ⇒ Object
Declare a sequential step.
-
.steps(*klasses, **opts) ⇒ Object
Declare parallel steps — all run simultaneously.
-
.storage(store = :not_set) ⇒ Object?
Set or get the default storage backend for checkpoint data.
-
.timeout(seconds = :not_set) ⇒ Integer?
Set or get the default timeout (in seconds) for steps in this graph.
Instance Method Summary collapse
- #call ⇒ Object (also: #run)
-
#initialize(input = nil, storage: nil) ⇒ Graph
constructor
A new instance of Graph.
- #resume(input:) ⇒ Object
Constructor Details
#initialize(input = nil, storage: nil) ⇒ Graph
Returns a new instance of Graph.
246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/ask/graph.rb', line 246 def initialize(input = nil, storage: nil) @input = input store = storage || self.class.storage || Ask::State::Memory.new hooks = self.class.lifecycle_hooks @runner = Runner.new(self.class.declarations, storage: store, hooks: hooks, graph_instance: self, default_timeout: self.class.timeout) @context = nil end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
259 260 261 |
# File 'lib/ask/graph.rb', line 259 def context @context end |
#runner ⇒ Object (readonly)
Returns the value of attribute runner.
258 259 260 |
# File 'lib/ask/graph.rb', line 258 def runner @runner end |
Class Method Details
.after_step(method_name) ⇒ Object
Register a callback to run after every successful step.
154 155 156 |
# File 'lib/ask/graph.rb', line 154 def after_step(method_name) lifecycle_hooks[:after_step] << method_name end |
.approve(klass, **opts) ⇒ Object
Declare a human-in-the-loop step. Runs the step, then pauses and waits for external input via #resume.
200 201 202 |
# File 'lib/ask/graph.rb', line 200 def approve(klass, **opts) declarations << build_declaration(:approve, klass, opts) end |
.before_step(method_name) ⇒ Object
Register a callback to run before every step.
148 149 150 |
# File 'lib/ask/graph.rb', line 148 def before_step(method_name) lifecycle_hooks[:before_step] << method_name end |
.call(input = nil, storage: nil) ⇒ Object
Create an instance and run it.
When input is a Context, the graph is run as a sub-graph:
the context data is exported, the graph runs with it, and results
are merged back into the original context automatically.
219 220 221 222 223 224 225 226 227 |
# File 'lib/ask/graph.rb', line 219 def call(input = nil, storage: nil) if input.is_a?(Context) ctx = new(input.export_data, storage: storage).call input.import(ctx) ctx else new(input, storage: storage).call end end |
.declarations ⇒ Object
All step declarations collected across the class hierarchy.
64 65 66 |
# File 'lib/ask/graph.rb', line 64 def declarations @declarations ||= [] end |
.inherited(subclass) ⇒ Object
Inherited hook — copies declarations to subclasses.
69 70 71 72 73 74 |
# File 'lib/ask/graph.rb', line 69 def inherited(subclass) super subclass.instance_variable_set(:@declarations, declarations.dup) subclass.instance_variable_set(:@lifecycle_hooks, lifecycle_hooks.transform_values(&:dup)) end |
.lifecycle_hooks ⇒ Object
--- Lifecycle hooks ---
142 143 144 |
# File 'lib/ask/graph.rb', line 142 def lifecycle_hooks @lifecycle_hooks ||= { before_step: [], after_step: [], on_failure: [] } end |
.on_failure(method_name) ⇒ Object
Register a callback to run when a step fails.
160 161 162 |
# File 'lib/ask/graph.rb', line 160 def on_failure(method_name) lifecycle_hooks[:on_failure] << method_name end |
.run ⇒ Object
Create an instance and run it.
When input is a Context, the graph is run as a sub-graph:
the context data is exported, the graph runs with it, and results
are merged back into the original context automatically.
228 229 230 231 232 233 234 235 236 |
# File 'lib/ask/graph.rb', line 228 def call(input = nil, storage: nil) if input.is_a?(Context) ctx = new(input.export_data, storage: storage).call input.import(ctx) ctx else new(input, storage: storage).call end end |
.step(klass, **opts) ⇒ Object
Declare a sequential step.
172 173 174 |
# File 'lib/ask/graph.rb', line 172 def step(klass, **opts) declarations << build_declaration(:step, klass, opts) end |
.steps(*klasses, **opts) ⇒ Object
Declare parallel steps — all run simultaneously.
181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/ask/graph.rb', line 181 def steps(*klasses, **opts) declarations << { classes: klasses, type: :steps, name: klasses.map { |k| k.name || k.to_s }.join(", "), if: opts[:if], unless: opts[:unless], description: opts[:description], timeout: opts[:timeout], retry: opts[:retry] } end |
.storage(store = :not_set) ⇒ Object?
Set or get the default storage backend for checkpoint data.
The storage object must respond to #set(key, value) and #get(key).
Setting on Ask::Graph itself applies to all graphs that don't set their own storage — a global default.
128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/ask/graph.rb', line 128 def storage(store = :not_set) if store == :not_set if instance_variable_defined?(:@storage) @storage elsif superclass.respond_to?(:storage) superclass.storage end else @storage = store end end |
.timeout(seconds = :not_set) ⇒ Integer?
Set or get the default timeout (in seconds) for steps in this graph.
Steps without an explicit timeout: option will inherit this value.
Setting on Ask::Graph itself applies to all graphs that don't set
their own default — a global default.
95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/ask/graph.rb', line 95 def timeout(seconds = :not_set) if seconds == :not_set if instance_variable_defined?(:@timeout) @timeout elsif superclass.respond_to?(:timeout) superclass.timeout end else @timeout = seconds end end |
Instance Method Details
#call ⇒ Object Also known as: run
261 262 263 264 265 266 267 268 |
# File 'lib/ask/graph.rb', line 261 def call(*) @context = Context.new(self, @input) @runner.run(@context) @context rescue => e raise unless e.is_a?(Paused) @context end |
#resume(input:) ⇒ Object
271 272 273 274 275 |
# File 'lib/ask/graph.rb', line 271 def resume(input:) @context.resume_input = input.to_s @runner.run(@context) @context end |