smith-agents 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +72 -0
- data/README.md +83 -0
- data/lib/smith/agent.rb +13 -0
- data/lib/smith/errors.rb +4 -0
- data/lib/smith/version.rb +2 -2
- data/lib/smith/workflow/composite/branch.rb +93 -0
- data/lib/smith/workflow/composite/branch_budget_contract.rb +41 -0
- data/lib/smith/workflow/composite/branch_contract.rb +102 -0
- data/lib/smith/workflow/composite/branch_execution.rb +124 -0
- data/lib/smith/workflow/composite/branch_failure.rb +91 -0
- data/lib/smith/workflow/composite/branch_outcome.rb +108 -0
- data/lib/smith/workflow/composite/budget_allocator.rb +78 -0
- data/lib/smith/workflow/composite/budget_math.rb +51 -0
- data/lib/smith/workflow/composite/contract.rb +100 -0
- data/lib/smith/workflow/composite/effects.rb +140 -0
- data/lib/smith/workflow/composite/effects_application.rb +36 -0
- data/lib/smith/workflow/composite/effects_baseline.rb +36 -0
- data/lib/smith/workflow/composite/effects_preflight.rb +102 -0
- data/lib/smith/workflow/composite/encoded_value_budget.rb +36 -0
- data/lib/smith/workflow/composite/enums.rb +27 -0
- data/lib/smith/workflow/composite/error.rb +54 -0
- data/lib/smith/workflow/composite/error_evidence.rb +61 -0
- data/lib/smith/workflow/composite/execution_contract.rb +73 -0
- data/lib/smith/workflow/composite/fanout_branch_contract.rb +61 -0
- data/lib/smith/workflow/composite/input.rb +53 -0
- data/lib/smith/workflow/composite/outcome_accumulator.rb +131 -0
- data/lib/smith/workflow/composite/outcome_set.rb +23 -0
- data/lib/smith/workflow/composite/payload.rb +128 -0
- data/lib/smith/workflow/composite/payload_digest.rb +28 -0
- data/lib/smith/workflow/composite/plan.rb +130 -0
- data/lib/smith/workflow/composite/plan_integrity.rb +52 -0
- data/lib/smith/workflow/composite/planner.rb +53 -0
- data/lib/smith/workflow/composite/preparation.rb +27 -0
- data/lib/smith/workflow/composite/reducer.rb +133 -0
- data/lib/smith/workflow/composite/reduction.rb +31 -0
- data/lib/smith/workflow/composite/value_budget.rb +90 -0
- data/lib/smith/workflow/composite_branch_execution_authorization.rb +52 -0
- data/lib/smith/workflow/deadline_enforcement.rb +22 -5
- data/lib/smith/workflow/execution.rb +16 -16
- data/lib/smith/workflow/fanout_execution.rb +19 -18
- data/lib/smith/workflow/message_value_normalizer.rb +11 -10
- data/lib/smith/workflow/parallel_execution.rb +41 -13
- data/lib/smith/workflow/prepared_branch_execution.rb +31 -0
- data/lib/smith/workflow/prepared_step_execution_authorization.rb +14 -29
- data/lib/smith/workflow/prepared_step_execution_scope.rb +81 -15
- data/lib/smith/workflow/process_local.rb +33 -0
- data/lib/smith/workflow/split_step_persistence/composite_branch_authorization.rb +99 -0
- data/lib/smith/workflow/split_step_persistence/composite_branch_effects.rb +41 -0
- data/lib/smith/workflow/split_step_persistence/composite_branch_execution.rb +95 -0
- data/lib/smith/workflow/split_step_persistence/composite_branch_outcome.rb +36 -0
- data/lib/smith/workflow/split_step_persistence/composite_execution.rb +39 -0
- data/lib/smith/workflow/split_step_persistence/composite_preparation.rb +57 -0
- data/lib/smith/workflow/split_step_persistence/composite_reduction_execution.rb +122 -0
- data/lib/smith/workflow/split_step_persistence/execution.rb +7 -53
- data/lib/smith/workflow/split_step_persistence/execution_authorization.rb +41 -21
- data/lib/smith/workflow/split_step_persistence/execution_authorization_issuance.rb +57 -0
- data/lib/smith/workflow/split_step_persistence/execution_binding_collector.rb +5 -0
- data/lib/smith/workflow/split_step_persistence/execution_binding_snapshot.rb +17 -3
- data/lib/smith/workflow/split_step_persistence/execution_lifecycle.rb +96 -0
- data/lib/smith/workflow/split_step_persistence/execution_verification.rb +17 -5
- data/lib/smith/workflow/split_step_persistence/preparation_claim.rb +8 -0
- data/lib/smith/workflow/split_step_persistence/subclass_boundary.rb +21 -5
- data/lib/smith/workflow/split_step_persistence.rb +14 -0
- data/lib/smith/workflow/step_context.rb +46 -0
- data/lib/smith/workflow/thread_context_snapshot.rb +103 -0
- data/lib/smith/workflow/transition.rb +16 -1
- data/lib/smith/workflow.rb +21 -1
- data/lib/smith.rb +3 -0
- metadata +46 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smith
|
|
4
|
+
class Workflow
|
|
5
|
+
module SplitStepPersistence
|
|
6
|
+
module ExecutionAuthorizationIssuance
|
|
7
|
+
def authorize_prepared_step_execution!
|
|
8
|
+
authorization = nil
|
|
9
|
+
handed_off = false
|
|
10
|
+
authorization = ExecutionAuthorizationIssuance
|
|
11
|
+
.instance_method(:interrupt_safe_prepared_step_authorization)
|
|
12
|
+
.bind_call(self)
|
|
13
|
+
handed_off = true
|
|
14
|
+
authorization
|
|
15
|
+
ensure
|
|
16
|
+
unless handed_off
|
|
17
|
+
ExecutionLifecycle
|
|
18
|
+
.instance_method(:release_failed_execution_authorization!)
|
|
19
|
+
.bind_call(self, authorization)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def with_prepared_step_execution_authorization
|
|
26
|
+
Thread.handle_interrupt(Object => :never) do
|
|
27
|
+
authorization = interrupt_safe_prepared_step_authorization
|
|
28
|
+
begin
|
|
29
|
+
Thread.handle_interrupt(Object => :immediate) { yield(authorization) }
|
|
30
|
+
ensure
|
|
31
|
+
ExecutionLifecycle
|
|
32
|
+
.instance_method(:release_failed_execution_authorization!)
|
|
33
|
+
.bind_call(self, authorization)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def interrupt_safe_prepared_step_authorization
|
|
39
|
+
Thread.handle_interrupt(Object => :never) do
|
|
40
|
+
verification_token = ExecutionAuthorization
|
|
41
|
+
.instance_method(:claim_framework_execution_verification!)
|
|
42
|
+
.bind_call(self)
|
|
43
|
+
begin
|
|
44
|
+
ExecutionAuthorization
|
|
45
|
+
.instance_method(:authorize_claimed_prepared_step_execution!)
|
|
46
|
+
.bind_call(self, verification_token)
|
|
47
|
+
ensure
|
|
48
|
+
ExecutionAuthorization
|
|
49
|
+
.instance_method(:restore_framework_execution_verification!)
|
|
50
|
+
.bind_call(self, verification_token)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -29,6 +29,11 @@ module Smith
|
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def capture_agent(name, workflow_class:, transition:, role:)
|
|
33
|
+
capture_binding(name, workflow_class:, transition:, role:)
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
|
|
32
37
|
def resolve!
|
|
33
38
|
@bindings = Agent::Registry.capture_bindings!(@requests.values)
|
|
34
39
|
|
|
@@ -14,9 +14,14 @@ module Smith
|
|
|
14
14
|
new(transition, workflow_class:).capture
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def
|
|
17
|
+
def self.capture_agent(transition, workflow_class:, name:, role:)
|
|
18
|
+
new(transition, workflow_class:, selected_agent: { name:, role: }).capture
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(transition, workflow_class:, selected_agent: nil)
|
|
18
22
|
@transition = transition
|
|
19
23
|
@workflow_class = workflow_class
|
|
24
|
+
@selected_agent = selected_agent
|
|
20
25
|
@bindings = ExecutionBindingCollector.new
|
|
21
26
|
@visited_workflows = {}.compare_by_identity
|
|
22
27
|
@visited_workflows[workflow_class] = true
|
|
@@ -27,8 +32,17 @@ module Smith
|
|
|
27
32
|
end
|
|
28
33
|
|
|
29
34
|
def capture
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
if @selected_agent
|
|
36
|
+
@bindings.capture_agent(
|
|
37
|
+
@selected_agent.fetch(:name),
|
|
38
|
+
workflow_class: @workflow_class,
|
|
39
|
+
transition: @transition,
|
|
40
|
+
role: @selected_agent.fetch(:role)
|
|
41
|
+
)
|
|
42
|
+
else
|
|
43
|
+
capture_transition(@transition, workflow_class: @workflow_class)
|
|
44
|
+
drain_workflow_queue
|
|
45
|
+
end
|
|
32
46
|
@bindings.resolve!
|
|
33
47
|
@bindings.freeze
|
|
34
48
|
@workflow_snapshots.freeze
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smith
|
|
4
|
+
class Workflow
|
|
5
|
+
module SplitStepPersistence
|
|
6
|
+
module ExecutionLifecycle
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def perform_authorized_prepared_step_execution!(authorization)
|
|
10
|
+
authorization = validate_split_step_execution_authorization!(authorization)
|
|
11
|
+
execution_thread = Thread.current
|
|
12
|
+
execute_prepared_step_lifecycle!(authorization, execution_thread) { yield(execution_thread) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def execute_prepared_step_lifecycle!(authorization, execution_thread)
|
|
16
|
+
execution_active = false
|
|
17
|
+
step = nil
|
|
18
|
+
result = nil
|
|
19
|
+
Thread.handle_interrupt(Object => :never) do
|
|
20
|
+
activate_split_step_execution!(authorization, execution_thread)
|
|
21
|
+
execution_active = true
|
|
22
|
+
Thread.handle_interrupt(Object => :immediate) do
|
|
23
|
+
step, result = yield(execution_thread)
|
|
24
|
+
end
|
|
25
|
+
ensure
|
|
26
|
+
finish_split_step_execution!(step, authorization, execution_thread) if execution_active
|
|
27
|
+
end
|
|
28
|
+
result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def activate_split_step_execution!(authorization, execution_thread)
|
|
32
|
+
Thread.handle_interrupt(Object => :never) do
|
|
33
|
+
@split_step_mutex.synchronize do
|
|
34
|
+
ensure_active_split_step_execution_authorization!(authorization)
|
|
35
|
+
ensure_split_step_definition_current!
|
|
36
|
+
ensure_prepared_split_step_transition_matches!
|
|
37
|
+
activate_prepared_step_execution_scope!(authorization)
|
|
38
|
+
publish_active_split_step_execution!(authorization, execution_thread)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def finish_split_step_execution!(step, authorization, execution_thread)
|
|
44
|
+
Thread.handle_interrupt(Object => :never) do
|
|
45
|
+
@split_step_mutex.synchronize do
|
|
46
|
+
reset_active_split_step_execution!(step) if active_split_step_execution?(execution_thread)
|
|
47
|
+
end
|
|
48
|
+
ensure
|
|
49
|
+
PreparedStepExecutionAuthorization.instance_method(:close_execution!).bind_call(authorization)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def ensure_active_split_step_execution_authorization!(authorization)
|
|
54
|
+
return if active_split_step_execution_authorization?(authorization)
|
|
55
|
+
|
|
56
|
+
raise WorkflowError, "the prepared-step execution authorization is no longer active"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def activate_prepared_step_execution_scope!(authorization)
|
|
60
|
+
PreparedStepExecutionAuthorization.instance_method(:activate_execution!).bind_call(authorization)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def publish_active_split_step_execution!(authorization, execution_thread)
|
|
64
|
+
@split_step_active_execution_authorization = authorization
|
|
65
|
+
@split_step_execution_result = nil
|
|
66
|
+
@split_step_phase = :executing
|
|
67
|
+
clear_split_step_execution_authorization!
|
|
68
|
+
@split_step_execution_thread = execution_thread
|
|
69
|
+
@split_step_advance_permit = true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def active_split_step_execution?(execution_thread)
|
|
73
|
+
@split_step_phase == :executing && @split_step_execution_thread.equal?(execution_thread)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def reset_active_split_step_execution!(step)
|
|
77
|
+
@split_step_execution_thread = nil
|
|
78
|
+
@split_step_advance_permit = false
|
|
79
|
+
@split_step_execution_previous_phase = nil
|
|
80
|
+
@split_step_active_execution_authorization = nil
|
|
81
|
+
@split_step_execution_result = nil
|
|
82
|
+
@split_step_phase = step ? :executed : :attempted
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def release_failed_execution_authorization!(authorization)
|
|
86
|
+
return unless authorization
|
|
87
|
+
return unless active_split_step_execution_authorization?(authorization)
|
|
88
|
+
|
|
89
|
+
ExecutionAuthorization
|
|
90
|
+
.instance_method(:release_prepared_step_execution!)
|
|
91
|
+
.bind_call(self, authorization)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -8,11 +8,18 @@ module Smith
|
|
|
8
8
|
|
|
9
9
|
def claim_split_step_execution_verification!
|
|
10
10
|
@split_step_mutex.synchronize do
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
DefinitionBoundary
|
|
12
|
+
.instance_method(:ensure_split_step_definition_current!)
|
|
13
|
+
.bind_call(self)
|
|
14
|
+
restart_safe = DefinitionBoundary
|
|
15
|
+
.instance_method(:restart_safe_split_step?)
|
|
16
|
+
.bind_call(self)
|
|
17
|
+
expected = restart_safe ? :dispatch_claimed : :prepared
|
|
13
18
|
raise WorkflowError, "no persisted step is prepared for execution" unless @split_step_phase == expected
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
ExecutionVerification
|
|
21
|
+
.instance_method(:ensure_prepared_split_step_transition_matches!)
|
|
22
|
+
.bind_call(self)
|
|
16
23
|
|
|
17
24
|
token = Object.new.freeze
|
|
18
25
|
@split_step_execution_previous_phase = expected
|
|
@@ -23,8 +30,13 @@ module Smith
|
|
|
23
30
|
end
|
|
24
31
|
|
|
25
32
|
def verify_claimed_split_step_execution!(verification_token)
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
restart_safe = DefinitionBoundary
|
|
34
|
+
.instance_method(:restart_safe_split_step?)
|
|
35
|
+
.bind_call(self)
|
|
36
|
+
if restart_safe
|
|
37
|
+
DispatchVerification
|
|
38
|
+
.instance_method(:verify_split_step_dispatch_available!)
|
|
39
|
+
.bind_call(self, verification_token)
|
|
28
40
|
else
|
|
29
41
|
verify_split_step_preparation_available!
|
|
30
42
|
end
|
|
@@ -33,6 +33,8 @@ module Smith
|
|
|
33
33
|
def finalize_split_step_preparation!(transition, transition_name, key, adapter, persistence_ttl)
|
|
34
34
|
transaction_identity = TransactionIdentity.capture(adapter)
|
|
35
35
|
transition_signature = TransitionContract.capture(transition)
|
|
36
|
+
namespace_preparer = PreparationClaim.instance_method(:prepare_composite_execution_namespace!)
|
|
37
|
+
namespace_preparer.bind_call(self, transition)
|
|
36
38
|
@split_step_mutex.synchronize do
|
|
37
39
|
unless active_split_step_preparation_claim?
|
|
38
40
|
raise WorkflowError, "the split-step preparation claim is no longer active"
|
|
@@ -65,6 +67,12 @@ module Smith
|
|
|
65
67
|
@split_step_preparation_thread.equal?(Thread.current)
|
|
66
68
|
end
|
|
67
69
|
|
|
70
|
+
def prepare_composite_execution_namespace!(transition)
|
|
71
|
+
return unless transition&.parallel? || transition&.fanout?
|
|
72
|
+
|
|
73
|
+
ArtifactIntegration.instance_method(:execution_namespace).bind_call(self)
|
|
74
|
+
end
|
|
75
|
+
|
|
68
76
|
def mark_split_step_prepared!(_adapter)
|
|
69
77
|
@split_step_mutex.synchronize do
|
|
70
78
|
phase = @split_step_transaction_identity ? :prepared_uncommitted : :prepared
|
|
@@ -68,15 +68,31 @@ module Smith
|
|
|
68
68
|
super
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
+
def authorize_prepared_step_execution!(...)
|
|
72
|
+
ExecutionAuthorizationIssuance.instance_method(:authorize_prepared_step_execution!).bind_call(self, ...)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def prepare_composite_step!(...)
|
|
76
|
+
CompositeExecution.instance_method(:prepare_composite_step!).bind_call(self, ...)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def execute_prepared_composite_branch!(...)
|
|
80
|
+
CompositeExecution.instance_method(:execute_prepared_composite_branch!).bind_call(self, ...)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def reduce_prepared_composite_step!(...)
|
|
84
|
+
CompositeExecution.instance_method(:reduce_prepared_composite_step!).bind_call(self, ...)
|
|
85
|
+
end
|
|
86
|
+
|
|
71
87
|
private
|
|
72
88
|
|
|
73
89
|
def smith_prepared_execution_active?
|
|
74
|
-
root_execution = @split_step_phase == :executing &&
|
|
75
|
-
@split_step_execution_thread.equal?(Thread.current)
|
|
76
|
-
return true if root_execution
|
|
77
|
-
|
|
78
90
|
authorization = @split_step_active_execution_authorization
|
|
79
|
-
authorization&.active_in_current_execution? == true
|
|
91
|
+
return true if authorization&.active_in_current_execution? == true
|
|
92
|
+
|
|
93
|
+
authorization = @split_step_execution_authorization
|
|
94
|
+
@split_step_phase == :execution_authorized &&
|
|
95
|
+
authorization&.issued_in_current_process? == true
|
|
80
96
|
end
|
|
81
97
|
|
|
82
98
|
def effective_persistence_ttl
|
|
@@ -36,7 +36,14 @@ require_relative "split_step_persistence/dispatch_confirmation"
|
|
|
36
36
|
require_relative "split_step_persistence/execution_verification"
|
|
37
37
|
require_relative "split_step_persistence/execution_binding_snapshot"
|
|
38
38
|
require_relative "split_step_persistence/execution_authorization"
|
|
39
|
+
require_relative "split_step_persistence/execution_authorization_issuance"
|
|
39
40
|
require_relative "split_step_persistence/execution_result_capture"
|
|
41
|
+
require_relative "split_step_persistence/execution_lifecycle"
|
|
42
|
+
require_relative "split_step_persistence/composite_preparation"
|
|
43
|
+
require_relative "split_step_persistence/composite_branch_authorization"
|
|
44
|
+
require_relative "split_step_persistence/composite_branch_execution"
|
|
45
|
+
require_relative "split_step_persistence/composite_reduction_execution"
|
|
46
|
+
require_relative "split_step_persistence/composite_execution"
|
|
40
47
|
require_relative "split_step_persistence/execution"
|
|
41
48
|
require_relative "split_step_persistence/checkpoint_state"
|
|
42
49
|
require_relative "split_step_persistence/checkpoint"
|
|
@@ -69,7 +76,14 @@ module Smith
|
|
|
69
76
|
include DispatchConfirmation
|
|
70
77
|
include ExecutionVerification
|
|
71
78
|
include ExecutionAuthorization
|
|
79
|
+
include ExecutionAuthorizationIssuance
|
|
72
80
|
include ExecutionResultCapture
|
|
81
|
+
include ExecutionLifecycle
|
|
82
|
+
include CompositePreparation
|
|
83
|
+
include CompositeBranchAuthorization
|
|
84
|
+
include CompositeBranchExecution
|
|
85
|
+
include CompositeReductionExecution
|
|
86
|
+
include CompositeExecution
|
|
73
87
|
include Execution
|
|
74
88
|
include CheckpointState
|
|
75
89
|
include Checkpoint
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "thread_context_snapshot"
|
|
4
|
+
|
|
5
|
+
module Smith
|
|
6
|
+
class Workflow
|
|
7
|
+
module StepContext
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def with_step_context(transition, &block)
|
|
11
|
+
ThreadContextSnapshot.new.around do
|
|
12
|
+
setup_step_context
|
|
13
|
+
Thread.handle_interrupt(Object => :immediate, &block)
|
|
14
|
+
rescue StandardError => e
|
|
15
|
+
@outcome = nil
|
|
16
|
+
GuardrailIntegration.instance_method(:handle_step_failure).bind_call(self, transition, e)
|
|
17
|
+
ensure
|
|
18
|
+
teardown_step_context
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def within_raw_step_context(&block)
|
|
23
|
+
ThreadContextSnapshot.new.around do
|
|
24
|
+
setup_step_context
|
|
25
|
+
Thread.handle_interrupt(Object => :immediate, &block)
|
|
26
|
+
ensure
|
|
27
|
+
teardown_step_context
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def setup_step_context
|
|
32
|
+
Tool.current_deadline = wall_clock_deadline
|
|
33
|
+
Tool.current_ledger = @ledger
|
|
34
|
+
Tool.current_tool_result_collector = tool_result_collector
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def teardown_step_context
|
|
38
|
+
Tool.current_guardrails = nil
|
|
39
|
+
Tool.current_deadline = nil
|
|
40
|
+
Tool.current_ledger = nil
|
|
41
|
+
Tool.current_tool_result_collector = nil
|
|
42
|
+
Smith.scoped_artifacts = nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry-initializer"
|
|
4
|
+
|
|
5
|
+
require_relative "process_local"
|
|
6
|
+
|
|
7
|
+
module Smith
|
|
8
|
+
class Workflow
|
|
9
|
+
class ThreadContextSnapshot
|
|
10
|
+
extend Dry::Initializer
|
|
11
|
+
include ProcessLocal
|
|
12
|
+
|
|
13
|
+
TOOL_ATTRIBUTES = %i[
|
|
14
|
+
current_guardrails
|
|
15
|
+
current_deadline
|
|
16
|
+
current_ledger
|
|
17
|
+
current_tool_call_allowance
|
|
18
|
+
current_tool_result_collector
|
|
19
|
+
].freeze
|
|
20
|
+
THREAD_KEYS = %i[
|
|
21
|
+
smith_call_deadline
|
|
22
|
+
smith_call_ledger
|
|
23
|
+
smith_failed_agent_results
|
|
24
|
+
smith_last_agent_result
|
|
25
|
+
smith_parallel_agent_binding
|
|
26
|
+
].freeze
|
|
27
|
+
|
|
28
|
+
option :tool_attributes, default: proc { TOOL_ATTRIBUTES }
|
|
29
|
+
option :thread_keys, default: proc { THREAD_KEYS }
|
|
30
|
+
option :scoped_artifacts, default: proc { true }
|
|
31
|
+
|
|
32
|
+
def initialize(...)
|
|
33
|
+
super
|
|
34
|
+
@tool_attributes = tool_attributes.dup.freeze
|
|
35
|
+
@thread_keys = thread_keys.dup.freeze
|
|
36
|
+
validate_selection!
|
|
37
|
+
@tool_values = capture_tool_values
|
|
38
|
+
@thread_values = capture_thread_values
|
|
39
|
+
@artifact_store = Smith.scoped_artifacts if scoped_artifacts
|
|
40
|
+
@process_id = Process.pid
|
|
41
|
+
@thread = Thread.current
|
|
42
|
+
@fiber = Fiber.current
|
|
43
|
+
@restored = false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def restore!
|
|
47
|
+
ensure_owner!
|
|
48
|
+
Thread.handle_interrupt(Object => :never) { restore_context! }
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def around
|
|
53
|
+
ensure_owner!
|
|
54
|
+
Thread.handle_interrupt(Object => :never) do
|
|
55
|
+
raise WorkflowError, "thread context snapshot has already been restored" if @restored
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
yield
|
|
59
|
+
ensure
|
|
60
|
+
restore_context!
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def restore_context!
|
|
68
|
+
raise WorkflowError, "thread context snapshot has already been restored" if @restored
|
|
69
|
+
|
|
70
|
+
@tool_values.each { |attribute, value| Tool.public_send("#{attribute}=", value) }
|
|
71
|
+
@thread_values.each { |key, value| Thread.current[key] = value }
|
|
72
|
+
Smith.scoped_artifacts = @artifact_store if scoped_artifacts
|
|
73
|
+
@restored = true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def capture_tool_values
|
|
77
|
+
tool_attributes.to_h { |attribute| [attribute, Tool.public_send(attribute)] }.freeze
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def capture_thread_values
|
|
81
|
+
thread_keys.to_h { |key| [key, Thread.current[key]] }.freeze
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def ensure_owner!
|
|
85
|
+
owner = @process_id == Process.pid && @thread.equal?(Thread.current) && @fiber.equal?(Fiber.current)
|
|
86
|
+
return if owner
|
|
87
|
+
|
|
88
|
+
raise WorkflowError, "thread context snapshot belongs to another process, thread, or fiber"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def validate_selection!
|
|
92
|
+
unless (tool_attributes - TOOL_ATTRIBUTES).empty?
|
|
93
|
+
raise ArgumentError, "thread context snapshot contains unsupported tool attributes"
|
|
94
|
+
end
|
|
95
|
+
return if (thread_keys - THREAD_KEYS).empty?
|
|
96
|
+
|
|
97
|
+
raise ArgumentError, "thread context snapshot contains unsupported thread keys"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private_constant :ThreadContextSnapshot
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -111,7 +111,11 @@ module Smith
|
|
|
111
111
|
def fan_out(branches:)
|
|
112
112
|
validate_execution_primitive_conflict!("fan_out")
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
normalized = normalize_fanout_branches!(branches)
|
|
115
|
+
@fanout_branch_lookup = normalized.to_h do |key, agent|
|
|
116
|
+
[key.to_s.freeze, [key, agent].freeze]
|
|
117
|
+
end.freeze
|
|
118
|
+
config = { branches: normalized }.freeze
|
|
115
119
|
|
|
116
120
|
@fanout_config = config
|
|
117
121
|
commit_execution_primitive!("fan_out")
|
|
@@ -156,6 +160,17 @@ module Smith
|
|
|
156
160
|
!@fanout_config.nil?
|
|
157
161
|
end
|
|
158
162
|
|
|
163
|
+
def fetch_fanout_agent!(branch_key)
|
|
164
|
+
fetch_fanout_branch!(branch_key).last
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def fetch_fanout_branch!(branch_key)
|
|
168
|
+
@fanout_branch_lookup.fetch(branch_key.to_s) do
|
|
169
|
+
raise WorkflowError, "fan_out branch #{branch_key.inspect} is not declared"
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
private :fetch_fanout_agent!, :fetch_fanout_branch!
|
|
173
|
+
|
|
159
174
|
def optimized?
|
|
160
175
|
!@optimization_config.nil?
|
|
161
176
|
end
|
data/lib/smith/workflow.rb
CHANGED
|
@@ -52,6 +52,9 @@ module Smith
|
|
|
52
52
|
"Smith::AgentError" => ->(s) { Smith::AgentError.new(s[:error_message]) },
|
|
53
53
|
"Smith::DeadlineExceeded" => ->(s) { Smith::DeadlineExceeded.new(s[:error_message]) },
|
|
54
54
|
"Smith::WorkflowError" => ->(s) { Smith::WorkflowError.new(s[:error_message]) },
|
|
55
|
+
"Smith::Workflow::Composite::BranchFailure" => lambda { |s|
|
|
56
|
+
Smith::Workflow::Composite::BranchFailure.from_details(s[:error_details])
|
|
57
|
+
},
|
|
55
58
|
# Smith errors with non-message constructors map to compatible
|
|
56
59
|
# superclass — message preserved, original metadata (agent_name,
|
|
57
60
|
# model_used, requested_name, workflow_class, origin_state) lossy
|
|
@@ -493,5 +496,22 @@ Smith::Workflow::SplitStepPersistence::SubclassBoundary.protect_execution_path!(
|
|
|
493
496
|
Smith::Workflow::EventIntegration,
|
|
494
497
|
Smith::Workflow::ArtifactIntegration,
|
|
495
498
|
Smith::Workflow::DataVolumePolicy,
|
|
496
|
-
Smith::Workflow::DeadlineEnforcement
|
|
499
|
+
Smith::Workflow::DeadlineEnforcement,
|
|
500
|
+
Smith::Workflow::SplitStepPersistence::DefinitionBoundary,
|
|
501
|
+
Smith::Workflow::SplitStepPersistence::ExecutionVerification,
|
|
502
|
+
Smith::Workflow::SplitStepPersistence::ExecutionAuthorization,
|
|
503
|
+
Smith::Workflow::SplitStepPersistence::ExecutionAuthorizationIssuance,
|
|
504
|
+
Smith::Workflow::SplitStepPersistence::ExecutionLifecycle,
|
|
505
|
+
Smith::Workflow::Composite::Contract,
|
|
506
|
+
Smith::Workflow::Composite::BranchContract,
|
|
507
|
+
Smith::Workflow::Composite::FanoutBranchContract,
|
|
508
|
+
Smith::Workflow::Composite::BranchBudgetContract,
|
|
509
|
+
Smith::Workflow::Composite::ExecutionContract,
|
|
510
|
+
Smith::Workflow::SplitStepPersistence::CompositeBranchEffects,
|
|
511
|
+
Smith::Workflow::SplitStepPersistence::CompositeBranchOutcome,
|
|
512
|
+
Smith::Workflow::SplitStepPersistence::CompositePreparation,
|
|
513
|
+
Smith::Workflow::SplitStepPersistence::CompositeBranchAuthorization,
|
|
514
|
+
Smith::Workflow::SplitStepPersistence::CompositeBranchExecution,
|
|
515
|
+
Smith::Workflow::SplitStepPersistence::CompositeReductionExecution,
|
|
516
|
+
Smith::Workflow::SplitStepPersistence::CompositeExecution
|
|
497
517
|
)
|
data/lib/smith.rb
CHANGED
|
@@ -267,6 +267,8 @@ require_relative "smith/workflow/graph_dsl"
|
|
|
267
267
|
require_relative "smith/workflow/run_result"
|
|
268
268
|
require_relative "smith/workflow/agent_result"
|
|
269
269
|
require_relative "smith/workflow/usage_entry"
|
|
270
|
+
require_relative "smith/workflow/process_local"
|
|
271
|
+
require_relative "smith/workflow/thread_context_snapshot"
|
|
270
272
|
require_relative "smith/workflow/branch_env"
|
|
271
273
|
require_relative "smith/workflow/optimization_state"
|
|
272
274
|
require_relative "smith/workflow/orchestration_state"
|
|
@@ -274,6 +276,7 @@ require_relative "smith/workflow/worker_execution"
|
|
|
274
276
|
require_relative "smith/workflow/dsl"
|
|
275
277
|
require_relative "smith/workflow/definition_identity"
|
|
276
278
|
require_relative "smith/workflow/prepared_step_dispatch"
|
|
279
|
+
require_relative "smith/workflow/prepared_branch_execution"
|
|
277
280
|
require_relative "smith/workflow/message_value_normalizer"
|
|
278
281
|
require_relative "smith/workflow/message_admission"
|
|
279
282
|
require_relative "smith/workflow/message_batch"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smith-agents
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Ralak
|
|
@@ -257,6 +257,38 @@ files:
|
|
|
257
257
|
- lib/smith/workflow/branch_env.rb
|
|
258
258
|
- lib/smith/workflow/budget_integration.rb
|
|
259
259
|
- lib/smith/workflow/claim.rb
|
|
260
|
+
- lib/smith/workflow/composite/branch.rb
|
|
261
|
+
- lib/smith/workflow/composite/branch_budget_contract.rb
|
|
262
|
+
- lib/smith/workflow/composite/branch_contract.rb
|
|
263
|
+
- lib/smith/workflow/composite/branch_execution.rb
|
|
264
|
+
- lib/smith/workflow/composite/branch_failure.rb
|
|
265
|
+
- lib/smith/workflow/composite/branch_outcome.rb
|
|
266
|
+
- lib/smith/workflow/composite/budget_allocator.rb
|
|
267
|
+
- lib/smith/workflow/composite/budget_math.rb
|
|
268
|
+
- lib/smith/workflow/composite/contract.rb
|
|
269
|
+
- lib/smith/workflow/composite/effects.rb
|
|
270
|
+
- lib/smith/workflow/composite/effects_application.rb
|
|
271
|
+
- lib/smith/workflow/composite/effects_baseline.rb
|
|
272
|
+
- lib/smith/workflow/composite/effects_preflight.rb
|
|
273
|
+
- lib/smith/workflow/composite/encoded_value_budget.rb
|
|
274
|
+
- lib/smith/workflow/composite/enums.rb
|
|
275
|
+
- lib/smith/workflow/composite/error.rb
|
|
276
|
+
- lib/smith/workflow/composite/error_evidence.rb
|
|
277
|
+
- lib/smith/workflow/composite/execution_contract.rb
|
|
278
|
+
- lib/smith/workflow/composite/fanout_branch_contract.rb
|
|
279
|
+
- lib/smith/workflow/composite/input.rb
|
|
280
|
+
- lib/smith/workflow/composite/outcome_accumulator.rb
|
|
281
|
+
- lib/smith/workflow/composite/outcome_set.rb
|
|
282
|
+
- lib/smith/workflow/composite/payload.rb
|
|
283
|
+
- lib/smith/workflow/composite/payload_digest.rb
|
|
284
|
+
- lib/smith/workflow/composite/plan.rb
|
|
285
|
+
- lib/smith/workflow/composite/plan_integrity.rb
|
|
286
|
+
- lib/smith/workflow/composite/planner.rb
|
|
287
|
+
- lib/smith/workflow/composite/preparation.rb
|
|
288
|
+
- lib/smith/workflow/composite/reducer.rb
|
|
289
|
+
- lib/smith/workflow/composite/reduction.rb
|
|
290
|
+
- lib/smith/workflow/composite/value_budget.rb
|
|
291
|
+
- lib/smith/workflow/composite_branch_execution_authorization.rb
|
|
260
292
|
- lib/smith/workflow/data_volume_policy.rb
|
|
261
293
|
- lib/smith/workflow/deadline_enforcement.rb
|
|
262
294
|
- lib/smith/workflow/definition_identity.rb
|
|
@@ -325,12 +357,14 @@ files:
|
|
|
325
357
|
- lib/smith/workflow/parallel_execution.rb
|
|
326
358
|
- lib/smith/workflow/persistence.rb
|
|
327
359
|
- lib/smith/workflow/pipeline.rb
|
|
360
|
+
- lib/smith/workflow/prepared_branch_execution.rb
|
|
328
361
|
- lib/smith/workflow/prepared_step.rb
|
|
329
362
|
- lib/smith/workflow/prepared_step_dispatch.rb
|
|
330
363
|
- lib/smith/workflow/prepared_step_execution_authorization.rb
|
|
331
364
|
- lib/smith/workflow/prepared_step_execution_result.rb
|
|
332
365
|
- lib/smith/workflow/prepared_step_execution_scope.rb
|
|
333
366
|
- lib/smith/workflow/prepared_step_recovery.rb
|
|
367
|
+
- lib/smith/workflow/process_local.rb
|
|
334
368
|
- lib/smith/workflow/retry_execution.rb
|
|
335
369
|
- lib/smith/workflow/router.rb
|
|
336
370
|
- lib/smith/workflow/run_result.rb
|
|
@@ -340,6 +374,13 @@ files:
|
|
|
340
374
|
- lib/smith/workflow/split_step_persistence/canonical_payload_digest.rb
|
|
341
375
|
- lib/smith/workflow/split_step_persistence/checkpoint.rb
|
|
342
376
|
- lib/smith/workflow/split_step_persistence/checkpoint_state.rb
|
|
377
|
+
- lib/smith/workflow/split_step_persistence/composite_branch_authorization.rb
|
|
378
|
+
- lib/smith/workflow/split_step_persistence/composite_branch_effects.rb
|
|
379
|
+
- lib/smith/workflow/split_step_persistence/composite_branch_execution.rb
|
|
380
|
+
- lib/smith/workflow/split_step_persistence/composite_branch_outcome.rb
|
|
381
|
+
- lib/smith/workflow/split_step_persistence/composite_execution.rb
|
|
382
|
+
- lib/smith/workflow/split_step_persistence/composite_preparation.rb
|
|
383
|
+
- lib/smith/workflow/split_step_persistence/composite_reduction_execution.rb
|
|
343
384
|
- lib/smith/workflow/split_step_persistence/definition_boundary.rb
|
|
344
385
|
- lib/smith/workflow/split_step_persistence/dispatch_boundary.rb
|
|
345
386
|
- lib/smith/workflow/split_step_persistence/dispatch_claim.rb
|
|
@@ -347,8 +388,10 @@ files:
|
|
|
347
388
|
- lib/smith/workflow/split_step_persistence/dispatch_verification.rb
|
|
348
389
|
- lib/smith/workflow/split_step_persistence/execution.rb
|
|
349
390
|
- lib/smith/workflow/split_step_persistence/execution_authorization.rb
|
|
391
|
+
- lib/smith/workflow/split_step_persistence/execution_authorization_issuance.rb
|
|
350
392
|
- lib/smith/workflow/split_step_persistence/execution_binding_collector.rb
|
|
351
393
|
- lib/smith/workflow/split_step_persistence/execution_binding_snapshot.rb
|
|
394
|
+
- lib/smith/workflow/split_step_persistence/execution_lifecycle.rb
|
|
352
395
|
- lib/smith/workflow/split_step_persistence/execution_result_capture.rb
|
|
353
396
|
- lib/smith/workflow/split_step_persistence/execution_verification.rb
|
|
354
397
|
- lib/smith/workflow/split_step_persistence/execution_workflow_snapshot.rb
|
|
@@ -371,7 +414,9 @@ files:
|
|
|
371
414
|
- lib/smith/workflow/split_step_persistence/transition_contract_signature.rb
|
|
372
415
|
- lib/smith/workflow/split_step_persistence/transition_contract_structured_values.rb
|
|
373
416
|
- lib/smith/workflow/step_completion.rb
|
|
417
|
+
- lib/smith/workflow/step_context.rb
|
|
374
418
|
- lib/smith/workflow/string_snapshot.rb
|
|
419
|
+
- lib/smith/workflow/thread_context_snapshot.rb
|
|
375
420
|
- lib/smith/workflow/transition.rb
|
|
376
421
|
- lib/smith/workflow/transition_actionability.rb
|
|
377
422
|
- lib/smith/workflow/usage_entry.rb
|