Module: Smith::Workflow::DSL::ClassMethods
- Defined in:
- lib/smith/workflow/dsl.rb,
lib/smith/workflow/pipeline.rb
Instance Method Summary collapse
- #budget(**opts) ⇒ Object
- #context_manager(klass = nil) ⇒ Object
- #find_transition(name) ⇒ Object
- #from_state(hash) ⇒ Object
- #guardrails(klass = nil) ⇒ Object
-
#idempotency_mode(mode = nil) ⇒ Object
Controls whether run_persisted! / advance_persisted! stamp a step_in_progress marker before each advance and clear it afterward.
- #inherited(subclass) ⇒ Object
- #initial_state(name = nil) ⇒ Object
- #max_transitions(count = nil) ⇒ Object
-
#migrate_from(version, &block) ⇒ Object
Register a one-step migration from stored version N to N+1.
- #migrations ⇒ Object
- #persistence_key(&block) ⇒ Object
-
#persistence_schema_version(version = nil) ⇒ Object
Schema version stamped into every persisted payload’s :schema_version key.
-
#persistence_ttl(seconds = nil) ⇒ Object
Per-workflow TTL override (in seconds).
- #pipeline(name, from:, to:) ⇒ Object
- #seed_messages(&block) ⇒ Object
-
#seed_validation(mode = nil) ⇒ Object
Controls whether restore validates that the seed_messages builder still produces the same digest as when this workflow was originally persisted.
- #state(name) ⇒ Object
- #transition(name, from:, to:) ⇒ Object
- #transitions_from(state) ⇒ Object
Instance Method Details
#budget(**opts) ⇒ Object
47 48 49 50 51 |
# File 'lib/smith/workflow/dsl.rb', line 47 def budget(**opts) return @budget_config if opts.empty? @budget_config = opts end |
#context_manager(klass = nil) ⇒ Object
65 66 67 68 69 |
# File 'lib/smith/workflow/dsl.rb', line 65 def context_manager(klass = nil) return @context_manager_class if klass.nil? @context_manager_class = klass end |
#find_transition(name) ⇒ Object
202 203 204 |
# File 'lib/smith/workflow/dsl.rb', line 202 def find_transition(name) (@transitions || {})[name] end |
#from_state(hash) ⇒ Object
206 207 208 209 210 |
# File 'lib/smith/workflow/dsl.rb', line 206 def from_state(hash) workflow = allocate workflow.send(:restore_state, hash) workflow end |
#guardrails(klass = nil) ⇒ Object
59 60 61 62 63 |
# File 'lib/smith/workflow/dsl.rb', line 59 def guardrails(klass = nil) return @guardrails_class if klass.nil? @guardrails_class = klass end |
#idempotency_mode(mode = nil) ⇒ Object
Controls whether run_persisted! / advance_persisted! stamp a step_in_progress marker before each advance and clear it afterward.
Modes:
:lax (default) no marker stamping; restore never raises.
Safe when agent calls and tools are idempotent, so
re-running a step on restore is harmless.
:strict marker is persisted before each advance and cleared
after. Restore raises
Smith::StepInProgressOnRestore when the marker is
set, indicating a previous worker crashed mid-step
and re-running could double-execute non-idempotent
agent calls or tools.
162 163 164 165 166 167 168 169 170 |
# File 'lib/smith/workflow/dsl.rb', line 162 def idempotency_mode(mode = nil) return @idempotency_mode || :lax if mode.nil? unless %i[strict lax].include?(mode) raise ArgumentError, "idempotency_mode must be :strict or :lax, got #{mode.inspect}" end @idempotency_mode = mode end |
#inherited(subclass) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/smith/workflow/dsl.rb', line 11 def inherited(subclass) super subclass.instance_variable_set(:@states, (@states || []).dup) subclass.instance_variable_set(:@transitions, (@transitions || {}).dup) subclass.instance_variable_set(:@initial_state_name, @initial_state_name) subclass.instance_variable_set(:@budget_config, @budget_config&.dup) subclass.instance_variable_set(:@max_transitions_count, @max_transitions_count) subclass.instance_variable_set(:@guardrails_class, @guardrails_class) subclass.instance_variable_set(:@context_manager_class, @context_manager_class) subclass.instance_variable_set(:@seed_messages_builder, @seed_messages_builder) subclass.instance_variable_set(:@persistence_key_builder, @persistence_key_builder) subclass.instance_variable_set(:@persistence_schema_version, @persistence_schema_version) subclass.instance_variable_set(:@migrations, (@migrations || {}).dup) subclass.instance_variable_set(:@seed_validation_mode, @seed_validation_mode) subclass.instance_variable_set(:@idempotency_mode, @idempotency_mode) subclass.instance_variable_set(:@persistence_ttl, @persistence_ttl) end |
#initial_state(name = nil) ⇒ Object
29 30 31 32 33 34 |
# File 'lib/smith/workflow/dsl.rb', line 29 def initial_state(name = nil) return @initial_state_name if name.nil? @initial_state_name = name state(name) end |
#max_transitions(count = nil) ⇒ Object
53 54 55 56 57 |
# File 'lib/smith/workflow/dsl.rb', line 53 def max_transitions(count = nil) return @max_transitions_count if count.nil? @max_transitions_count = count end |
#migrate_from(version, &block) ⇒ Object
Register a one-step migration from stored version N to N+1. The block receives the persisted payload Hash (top-level keys already symbolized) and must return the migrated payload. Bumping the :schema_version key is the migration’s responsibility but Smith advances defensively if the block omits it, so migrations stay loop-free.
107 108 109 110 111 112 113 114 115 116 |
# File 'lib/smith/workflow/dsl.rb', line 107 def migrate_from(version, &block) raise ArgumentError, "migrate_from requires a block" unless block unless version.is_a?(Integer) && version >= 1 raise ArgumentError, "migrate_from version must be a positive Integer, got #{version.inspect}" end @migrations ||= {} @migrations[version] = block end |
#migrations ⇒ Object
118 119 120 |
# File 'lib/smith/workflow/dsl.rb', line 118 def migrations @migrations || {} end |
#persistence_key(&block) ⇒ Object
77 78 79 80 81 |
# File 'lib/smith/workflow/dsl.rb', line 77 def persistence_key(&block) return @persistence_key_builder unless block_given? @persistence_key_builder = block end |
#persistence_schema_version(version = nil) ⇒ Object
Schema version stamped into every persisted payload’s :schema_version key. Restore compares the stored version with this value and either passes through (equal), applies registered migrate_from blocks one step at a time (stored less than current), or raises Smith::PersistenceSchemaMismatch (stored greater than current, or unbridged gap). Pre-versioning payloads (no :schema_version key) are treated as v1 for backward compatibility.
91 92 93 94 95 96 97 98 99 |
# File 'lib/smith/workflow/dsl.rb', line 91 def persistence_schema_version(version = nil) return @persistence_schema_version || 1 if version.nil? unless version.is_a?(Integer) && version >= 1 raise ArgumentError, "persistence_schema_version must be a positive Integer, got #{version.inspect}" end @persistence_schema_version = version end |
#persistence_ttl(seconds = nil) ⇒ Object
Per-workflow TTL override (in seconds). Takes precedence over Smith.config.persistence_ttl at persist! time. nil (default) means inherit the global config.
Hosts typically set this when different workflow classes have different durability horizons: e.g., short-lived UI sessions at 1.day.to_i, long-running research workflows at 30.days.to_i.
Wiring contract: when the resolved TTL is non-nil, Workflow#persist! forwards it to the adapter as a ‘ttl:` kwarg. Shipped adapters (Memory, RedisStore, CacheStore, ActiveRecordStore) accept this kwarg; external duck-typed adapters that implement only the bare REQUIRED_METHODS contract without a `ttl:` kwarg will only break when a host actually opts into TTL.
187 188 189 190 191 192 193 194 195 196 |
# File 'lib/smith/workflow/dsl.rb', line 187 def persistence_ttl(seconds = nil) return @persistence_ttl if seconds.nil? unless seconds.is_a?(Numeric) && seconds.positive? raise ArgumentError, "persistence_ttl must be a positive Numeric (seconds), got #{seconds.inspect}" end @persistence_ttl = seconds end |
#pipeline(name, from:, to:) ⇒ Object
111 112 113 |
# File 'lib/smith/workflow/pipeline.rb', line 111 def pipeline(name, from:, to:, &) Pipeline.new(name, from: from, to: to, &).compile!(self) end |
#seed_messages(&block) ⇒ Object
71 72 73 74 75 |
# File 'lib/smith/workflow/dsl.rb', line 71 def (&block) return @seed_messages_builder unless block_given? @seed_messages_builder = block end |
#seed_validation(mode = nil) ⇒ Object
Controls whether restore validates that the seed_messages builder still produces the same digest as when this workflow was originally persisted.
Modes:
:off (default) skip validation entirely. Recommended when
the seed builder is non-deterministic (timestamps,
UUIDs, request-scoped data) since drift would
surface on every restore.
:warn log a warning via Smith.config.logger on drift; do
not raise. Suitable for soft monitoring.
:strict raise Smith::SeedMismatch on drift. Suitable when
the seed builder is deterministic (system
instructions, static templates) and divergence
indicates a code change that would invalidate the
persisted conversation context.
138 139 140 141 142 143 144 145 146 |
# File 'lib/smith/workflow/dsl.rb', line 138 def seed_validation(mode = nil) return @seed_validation_mode || :off if mode.nil? unless %i[strict warn off].include?(mode) raise ArgumentError, "seed_validation must be :strict, :warn, or :off, got #{mode.inspect}" end @seed_validation_mode = mode end |
#state(name) ⇒ Object
36 37 38 39 40 |
# File 'lib/smith/workflow/dsl.rb', line 36 def state(name) @states ||= [] @states << name unless @states.include?(name) generate_fail_transition if name == :failed end |
#transition(name, from:, to:) ⇒ Object
42 43 44 45 |
# File 'lib/smith/workflow/dsl.rb', line 42 def transition(name, from:, to:, &) @transitions ||= {} @transitions[name] = Transition.new(name, from: from, to: to, &) end |
#transitions_from(state) ⇒ Object
198 199 200 |
# File 'lib/smith/workflow/dsl.rb', line 198 def transitions_from(state) (@transitions || {}).values.select { |t| t.from == state } end |