robot_lab-ractor 0.1.0 → 0.2.6

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.
@@ -50,21 +50,10 @@ module RobotLab
50
50
  remaining = specs_with_deps.dup
51
51
 
52
52
  until remaining.empty?
53
- ready, remaining = remaining.partition do |entry|
54
- deps = entry[:depends_on]
55
- deps == :none || deps == :optional ||
56
- Array(deps).all? { |d| completed.key?(d) }
57
- end
58
-
59
- raise RobotLab::Error, "Circular dependency or unresolvable deps in RactorNetworkScheduler" if ready.empty?
53
+ ready, remaining = partition_ready(remaining, completed)
54
+ raise RobotLab::Error, 'Circular dependency or unresolvable deps in RactorNetworkScheduler' if ready.empty?
60
55
 
61
- threads = ready.map do |entry|
62
- spec = entry[:spec]
63
- msg = completed.values.last || message
64
- Thread.new { [spec.name, execute_spec(spec, msg)] }.tap { |t| t.report_on_exception = false }
65
- end
66
-
67
- threads.each do |t|
56
+ dispatch_ready(ready, completed, message).each do |t|
68
57
  name, result = t.value
69
58
  completed[name] = result
70
59
  end
@@ -79,7 +68,39 @@ module RobotLab
79
68
 
80
69
  @closed = true
81
70
  @size.times { @work_q.push(nil) }
82
- @workers.each { |w| w.join rescue nil }
71
+ @workers.each do |w|
72
+ w.join
73
+ rescue StandardError
74
+ nil
75
+ end
76
+ end
77
+
78
+ # Called inside Ractor worker blocks — must be a class method.
79
+ def self.process_job(job)
80
+ spec = job.payload[:spec]
81
+ message = job.payload[:message]
82
+ job.reply_queue.push(build_and_run_robot(spec, message))
83
+ rescue StandardError => e
84
+ job.reply_queue.push(wrap_error(e))
85
+ end
86
+
87
+ def self.build_and_run_robot(spec, message)
88
+ config = spec.config_hash.empty? ? nil : RobotLab::RunConfig.new(**spec.config_hash.transform_keys(&:to_sym))
89
+ robot = RobotLab::Robot.new(
90
+ name: spec.name,
91
+ template: spec.template&.to_sym,
92
+ system_prompt: spec.system_prompt,
93
+ config: config
94
+ )
95
+ spec.hook_classes.each { |handler| robot.on(handler) }
96
+ robot.run(message).last_text_content.to_s.freeze
97
+ end
98
+
99
+ def self.wrap_error(err)
100
+ RobotLab::RactorJobError.new(
101
+ message: err.message.freeze,
102
+ backtrace: (err.backtrace || []).map(&:freeze).freeze
103
+ )
83
104
  end
84
105
 
85
106
  private
@@ -90,18 +111,16 @@ module RobotLab
90
111
  reply_q = RactorQueue.new(capacity: 1)
91
112
 
92
113
  job = RactorJob.new(
93
- id: SecureRandom.uuid.freeze,
94
- type: :robot,
95
- payload: RactorBoundary.freeze_deep({ spec: frozen_spec, message: frozen_message }),
114
+ id: SecureRandom.uuid.freeze,
115
+ type: :robot,
116
+ payload: RactorBoundary.freeze_deep({ spec: frozen_spec, message: frozen_message }),
96
117
  reply_queue: reply_q
97
118
  )
98
119
 
99
120
  @work_q.push(job)
100
121
  result = reply_q.pop
101
122
 
102
- if result.is_a?(RactorJobError)
103
- raise RobotLab::Error, "Robot '#{spec.name}' failed in Ractor: #{result.message}"
104
- end
123
+ raise RobotLab::Error, "Robot '#{spec.name}' failed in Ractor: #{result.message}" if result.is_a?(RactorJobError)
105
124
 
106
125
  result
107
126
  end
@@ -112,28 +131,24 @@ module RobotLab
112
131
  job = q.pop
113
132
  break if job.nil?
114
133
 
115
- begin
116
- spec = job.payload[:spec]
117
- message = job.payload[:message]
118
-
119
- robot = RobotLab::Robot.new(
120
- name: spec.name,
121
- template: spec.template ? spec.template.to_sym : nil,
122
- system_prompt: spec.system_prompt,
123
- config: spec.config_hash.empty? ? nil : RobotLab::RunConfig.new(**spec.config_hash.transform_keys(&:to_sym))
124
- )
125
-
126
- robot_result = robot.run(message)
127
- job.reply_queue.push(robot_result.last_text_content.to_s.freeze)
128
- rescue => e
129
- err = RobotLab::RactorJobError.new(
130
- message: e.message.freeze,
131
- backtrace: (e.backtrace || []).map(&:freeze).freeze
132
- )
133
- job.reply_queue.push(err)
134
- end
134
+ RobotLab::RactorNetworkScheduler.process_job(job)
135
135
  end
136
136
  end
137
137
  end
138
+
139
+ def partition_ready(remaining, completed)
140
+ remaining.partition do |entry|
141
+ deps = entry[:depends_on]
142
+ deps == :none || deps == :optional || Array(deps).all? { |d| completed.key?(d) }
143
+ end
144
+ end
145
+
146
+ def dispatch_ready(ready, completed, message)
147
+ ready.map do |entry|
148
+ spec = entry[:spec]
149
+ msg = completed.values.last || message
150
+ Thread.new { [spec.name, execute_spec(spec, msg)] }.tap { |t| t.report_on_exception = false }
151
+ end
152
+ end
138
153
  end
139
154
  end
@@ -39,27 +39,22 @@ module RobotLab
39
39
  # @return [Object] the tool's return value
40
40
  # @raise [ToolError] if the tool raises inside the Ractor
41
41
  def submit(tool_class_name, args)
42
- raise ToolError, "Pool is shut down" if @closed
42
+ raise ToolError, 'Pool is shut down' if @closed
43
43
 
44
44
  reply_q = RactorQueue.new(capacity: 1)
45
- payload = RactorBoundary.freeze_deep({
46
- tool_class: tool_class_name.to_s,
47
- args: args
48
- })
45
+ payload = RactorBoundary.freeze_deep({ tool_class: tool_class_name.to_s, args: args })
49
46
 
50
47
  job = RactorJob.new(
51
- id: SecureRandom.uuid.freeze,
52
- type: :tool,
53
- payload: payload,
48
+ id: SecureRandom.uuid.freeze,
49
+ type: :tool,
50
+ payload: payload,
54
51
  reply_queue: reply_q
55
52
  )
56
53
 
57
54
  @work_q.push(job)
58
55
  result = reply_q.pop
59
56
 
60
- if result.is_a?(RactorJobError)
61
- raise ToolError, "Tool '#{tool_class_name}' failed in Ractor: #{result.message}"
62
- end
57
+ raise ToolError, "Tool '#{tool_class_name}' failed in Ractor: #{result.message}" if result.is_a?(RactorJobError)
63
58
 
64
59
  result
65
60
  end
@@ -71,7 +66,28 @@ module RobotLab
71
66
 
72
67
  @closed = true
73
68
  @size.times { @work_q.push(nil) }
74
- @workers.each { |w| w.join rescue nil }
69
+ @workers.each do |w|
70
+ w.join
71
+ rescue StandardError
72
+ nil
73
+ end
74
+ end
75
+
76
+ # Called inside Ractor worker blocks — must be a class method.
77
+ def self.process_job(job)
78
+ tool_class = Object.const_get(job.payload[:tool_class])
79
+ result = tool_class.new.execute(**job.payload[:args].transform_keys(&:to_sym))
80
+ frozen_result = ::Ractor.make_shareable(result.frozen? ? result : result.dup.freeze)
81
+ job.reply_queue.push(frozen_result)
82
+ rescue StandardError => e
83
+ job.reply_queue.push(wrap_error(e))
84
+ end
85
+
86
+ def self.wrap_error(err)
87
+ RobotLab::RactorJobError.new(
88
+ message: err.message.freeze,
89
+ backtrace: (err.backtrace || []).map(&:freeze).freeze
90
+ )
75
91
  end
76
92
 
77
93
  private
@@ -82,19 +98,7 @@ module RobotLab
82
98
  job = q.pop
83
99
  break if job.nil?
84
100
 
85
- begin
86
- tool_class = Object.const_get(job.payload[:tool_class])
87
- tool = tool_class.new
88
- result = tool.execute(**job.payload[:args].transform_keys(&:to_sym))
89
- frozen_result = ::Ractor.make_shareable(result.frozen? ? result : result.dup.freeze)
90
- job.reply_queue.push(frozen_result)
91
- rescue => e
92
- err = RobotLab::RactorJobError.new(
93
- message: e.message.freeze,
94
- backtrace: (e.backtrace || []).map(&:freeze).freeze
95
- )
96
- job.reply_queue.push(err)
97
- end
101
+ RobotLab::RactorWorkerPool.process_job(job)
98
102
  end
99
103
  end
100
104
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: robot_lab-ractor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
@@ -10,7 +10,7 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: robot_lab
13
+ name: ractor_queue
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
@@ -24,7 +24,7 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
26
  - !ruby/object:Gem::Dependency
27
- name: ractor_queue
27
+ name: ractor-wrapper
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
@@ -38,19 +38,19 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  - !ruby/object:Gem::Dependency
41
- name: ractor-wrapper
41
+ name: robot_lab
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '0'
46
+ version: 0.2.0
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ">="
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0'
53
+ version: 0.2.0
54
54
  description: Provides RobotLab::RactorWorkerPool — a pool of Ruby Ractor workers for
55
55
  executing CPU-bound, Ractor-safe tools in parallel. Includes RactorBoundary (deep-freeze
56
56
  utility for crossing Ractor boundaries), RactorJob/RactorJobError/RobotSpec (frozen
@@ -64,7 +64,11 @@ extra_rdoc_files: []
64
64
  files:
65
65
  - ".envrc"
66
66
  - ".github/workflows/deploy-github-pages.yml"
67
+ - ".loki"
68
+ - ".quality/reek_baseline.txt"
69
+ - ".rubocop.yml"
67
70
  - CHANGELOG.md
71
+ - CLAUDE.md
68
72
  - LICENSE.txt
69
73
  - README.md
70
74
  - Rakefile
@@ -72,8 +76,11 @@ files:
72
76
  - docs/index.md
73
77
  - docs/superpowers/plans/2026-04-14-ractor-integration.md
74
78
  - docs/superpowers/specs/2026-04-14-ractor-integration-design.md
75
- - examples/29_ractor_tools.rb
76
- - examples/30_ractor_network.rb
79
+ - examples/01_ractor_tools.rb
80
+ - examples/02_ractor_network.rb
81
+ - examples/03_ractor_hooks.rb
82
+ - examples/common.rb
83
+ - examples/xyzzy.rb
77
84
  - lib/robot_lab/ractor.rb
78
85
  - lib/robot_lab/ractor/version.rb
79
86
  - lib/robot_lab/ractor_boundary.rb
@@ -104,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
111
  - !ruby/object:Gem::Version
105
112
  version: '0'
106
113
  requirements: []
107
- rubygems_version: 4.0.11
114
+ rubygems_version: 4.0.17
108
115
  specification_version: 4
109
116
  summary: Ractor-based parallel tool execution for RobotLab agents
110
117
  test_files: []