robot_lab-ractor 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24693d3b0371d41a878e7112c904e01bbf65d46f4cded26fe78b45856c49716c
4
- data.tar.gz: e7fdf7b457efb4e5359f9ccf36afd50823f2fe15d11e5c56d6dd2fe93ba9f959
3
+ metadata.gz: 25de3bcf1d2904408d989619f14f2204b556d5cc7412683793d1e108c986192f
4
+ data.tar.gz: 412b11c1532be8674b9ec1767471d42909dd20e98cf2ca6661f143dbeccbe68f
5
5
  SHA512:
6
- metadata.gz: 5ed65c66df3ef0d840ab8f4745cf2a3b2fe82c6216f0458480336ec3274fafde8ce8b2c610526de66f00cdf9473ab52961b6f818166b64752a911a377cb21a34
7
- data.tar.gz: 9024af8c7f52c239b8549e04eab3c9f852622264a3f3656aac4d41cfc72ccb41707b07ec7749622aaddb4c8ff95d21af0c7e0aa106bedf25c4a58d7de15e9095
6
+ metadata.gz: ce3e69d19db119d2f28e0c4b557826271f09169d8f5adaf811787f2ffba9bd637ace4aad2f5e8f525781d5c59c6e995bd851f26bac222a5e695d3ac7a6e08c1d
7
+ data.tar.gz: b3a6f73c02fd8e006dc71c1cea906adb1d47051c5c584d30b45ba374f9c43a036f0782a3f60a72fde5463cd78503cc7b0292a77ded5858b52d5ef9edbc693530
data/.rubocop.yml ADDED
@@ -0,0 +1,173 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ SuggestExtensions: false
4
+ TargetRubyVersion: 4.0
5
+ Exclude:
6
+ - 'examples/**/*'
7
+ - 'vendor/**/*'
8
+ - 'dead_code/**/*'
9
+
10
+ # ── Style: disabled cops ───────────────────────────────────────────────────
11
+ Style/StringLiterals:
12
+ Enabled: false
13
+
14
+ Style/StringLiteralsInInterpolation:
15
+ Enabled: false
16
+
17
+ Style/Documentation:
18
+ Enabled: false
19
+
20
+ # Ruby 4.0 freezes string literals by default
21
+ Style/FrozenStringLiteralComment:
22
+ Enabled: false
23
+
24
+ Style/IfUnlessModifier:
25
+ Enabled: false
26
+
27
+ Style/RescueModifier:
28
+ Enabled: false
29
+
30
+ Style/TrivialAccessors:
31
+ Enabled: false
32
+
33
+ Style/MultilineTernaryOperator:
34
+ Enabled: false
35
+
36
+ Style/SafeNavigation:
37
+ Enabled: false
38
+
39
+ Style/EmptyClassDefinition:
40
+ Enabled: false
41
+
42
+ Style/ClassAndModuleChildren:
43
+ Enabled: false
44
+
45
+ Style/RescueStandardError:
46
+ Enabled: false
47
+
48
+ Style/OneClassPerFile:
49
+ Enabled: false
50
+
51
+ # Both % and format/sprintf are acceptable
52
+ Style/FormatString:
53
+ Enabled: false
54
+
55
+ # String concatenation and interpolation are both acceptable
56
+ Style/StringConcatenation:
57
+ Enabled: false
58
+
59
+ # ── Layout ─────────────────────────────────────────────────────────────────
60
+ Layout/LineLength:
61
+ Max: 140
62
+
63
+ Layout/ExtraSpacing:
64
+ Enabled: false
65
+
66
+ Layout/HashAlignment:
67
+ Enabled: false
68
+
69
+ Layout/FirstHashElementIndentation:
70
+ Enabled: false
71
+
72
+ Layout/EmptyLineAfterGuardClause:
73
+ Enabled: false
74
+
75
+ # ── Naming ─────────────────────────────────────────────────────────────────
76
+ # Single-char params (c, e, n) are acceptable throughout
77
+ Naming/MethodParameterName:
78
+ Enabled: false
79
+
80
+ Naming/VariableNumber:
81
+ Exclude:
82
+ - 'test/**/*'
83
+
84
+ Naming/RescuedExceptionsVariableName:
85
+ Enabled: false
86
+
87
+ # set_results and similar explicit setters are clear and conventional
88
+ Naming/AccessorMethodName:
89
+ Enabled: false
90
+
91
+
92
+ # has_tool_calls? and similar are clear and conventional
93
+ Naming/PredicatePrefix:
94
+ Enabled: false
95
+
96
+ # Test helper methods don't need to follow predicate naming rules
97
+ Naming/PredicateMethod:
98
+ Exclude:
99
+ - 'test/**/*'
100
+
101
+ # ── Lint: relax noisy cops on intentional patterns ─────────────────────────
102
+ # Library and framework methods commonly accept args for API/documentation purposes
103
+ Lint/UnusedMethodArgument:
104
+ Enabled: false
105
+
106
+
107
+ Lint/EmptyBlock:
108
+ Exclude:
109
+ - 'test/**/*'
110
+
111
+ Lint/ConstantDefinitionInBlock:
112
+ Exclude:
113
+ - 'Rakefile'
114
+ - 'test/**/*'
115
+
116
+ # ── Gemspec ────────────────────────────────────────────────────────────────
117
+ Gemspec/DevelopmentDependencies:
118
+ EnforcedStyle: Gemfile
119
+
120
+ Gemspec/RequiredRubyVersion:
121
+ Enabled: false
122
+
123
+ Gemspec/OrderedDependencies:
124
+ Enabled: false
125
+
126
+ # ── Metrics ────────────────────────────────────────────────────────────────
127
+ # Framework-level code (routers, parsers, orchestrators) is inherently complex.
128
+ # Flog is the primary complexity gate — these RuboCop thresholds catch only
129
+ # egregious outliers without false-positiving every dispatch method.
130
+
131
+ Metrics/MethodLength:
132
+ Max: 35
133
+ CountAsOne:
134
+ - heredoc
135
+ - array
136
+ - hash
137
+ Exclude:
138
+ - 'test/**/*'
139
+
140
+ Metrics/AbcSize:
141
+ Max: 40
142
+ Exclude:
143
+ - 'test/**/*'
144
+
145
+ Metrics/ClassLength:
146
+ Max: 600
147
+ Exclude:
148
+ - 'test/**/*'
149
+
150
+ Metrics/ModuleLength:
151
+ Max: 200
152
+ Exclude:
153
+ - 'test/**/*'
154
+
155
+ Metrics/CyclomaticComplexity:
156
+ Max: 20
157
+ Exclude:
158
+ - 'test/**/*'
159
+
160
+ Metrics/PerceivedComplexity:
161
+ Max: 20
162
+ Exclude:
163
+ - 'test/**/*'
164
+
165
+ # Long method signatures with keyword args are a Ruby framework idiom
166
+ Metrics/ParameterLists:
167
+ Enabled: false
168
+
169
+ Metrics/BlockLength:
170
+ Exclude:
171
+ - 'Rakefile'
172
+ - '*.gemspec'
173
+ - 'test/**/*'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.1] - 2026-05-19
4
+
5
+ ### Added
6
+ - `RactorWorkerPool` — shared pool of Ractor workers; tools marked `ractor_safe true` are automatically routed through it instead of running inline
7
+ - `RactorNetworkScheduler` — DAG-aware parallel execution of robot networks using Ractors; activated via `parallel_mode: :ractor` on a network
8
+ - `RactorBoundary` — `freeze_deep` utility for making values Ractor-shareable; raises `RactorBoundaryError` for values that cannot cross Ractor boundaries
9
+ - `RactorMemoryProxy` — thread-safe proxy exposing `RobotLab::Memory` to Ractor workers via `Ractor::Wrapper`
10
+ - `RactorJob` — Ractor-shareable frozen job value object used internally by the pool and scheduler
11
+ - `ractor_safe` class-level DSL for `RubyLLM::Tool` / `RobotLab::Tool` subclasses; inherited by subclasses
12
+ - `RobotLab.ractor_pool` / `.shutdown_ractor_pool` — process-level pool lifecycle methods added to the `RobotLab` module
13
+ - Ractor Parallelism guide (`docs/guides/ractor-parallelism.md`) covering both CPU-bound tools and parallel network pipelines
14
+
15
+ ### Changed
16
+ - Version synchronized with robot_lab core 0.2.1
17
+
3
18
  ## [0.1.0] - 2026-05-07
4
19
 
5
20
  - Initial release
data/Rakefile CHANGED
@@ -1,8 +1,116 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "minitest/test_task"
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
5
 
6
- Minitest::TestTask.create
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb', 'test/**/test_*.rb'].exclude('**/*_helper.rb')
10
+ t.verbose = true
11
+ t.ruby_opts << '-rtest_helper'
12
+ end
7
13
 
8
14
  task default: :test
15
+
16
+ desc 'Run tests with verbose output'
17
+ task :test_verbose do
18
+ ENV['TESTOPTS'] = '--verbose'
19
+ Rake::Task[:test].invoke
20
+ end
21
+
22
+ desc 'Run a single test file'
23
+ task :test_file, [:file] do |_t, args|
24
+ ruby "test/#{args[:file]}"
25
+ end
26
+
27
+ desc 'Check code style with RuboCop'
28
+ task :rubocop do
29
+ sh 'bundle exec rubocop'
30
+ end
31
+
32
+ desc 'Auto-correct RuboCop offenses'
33
+ task :rubocop_fix do
34
+ sh 'bundle exec rubocop -a'
35
+ end
36
+
37
+ desc 'Check code complexity with Flog (warn >=20, fail >=50)'
38
+ task :flog_check do
39
+ require 'flog'
40
+
41
+ method_warn = 20.0
42
+ method_fail = 50.0
43
+
44
+ flogger = Flog.new(all: true)
45
+ flogger.flog(*Dir.glob('lib/**/*.rb'))
46
+
47
+ warnings = []
48
+ failures = []
49
+
50
+ flogger.each_by_score do |method, score|
51
+ next if method.end_with?('#none')
52
+
53
+ if score > method_fail
54
+ failures << "#{format('%.1f', score)}: #{method}"
55
+ elsif score > method_warn
56
+ warnings << "#{format('%.1f', score)}: #{method}"
57
+ end
58
+ end
59
+
60
+ unless warnings.empty?
61
+ puts "\nFlog warnings (#{method_warn}–#{method_fail}) — target for future refactoring:"
62
+ warnings.each { |v| puts " #{v}" }
63
+ end
64
+
65
+ if failures.empty?
66
+ puts "\nFlog: no methods exceed the failure threshold (>=#{method_fail})"
67
+ else
68
+ puts "\nFlog failures (>=#{method_fail}) — must be refactored:"
69
+ failures.each { |v| puts " #{v}" }
70
+ abort "\nFlog quality gate failed: #{failures.size} method(s) exceed #{method_fail}"
71
+ end
72
+ end
73
+
74
+ desc 'Run all quality checks: tests (with coverage), RuboCop, and Flog'
75
+ task :quality do
76
+ results = {}
77
+
78
+ puts "\n#{'=' * 60}"
79
+ puts 'Quality Gate: Tests + Coverage'
80
+ puts '=' * 60
81
+ results[:tests] = system('bundle exec rake test') ? :pass : :fail
82
+
83
+ puts "\n#{'=' * 60}"
84
+ puts 'Quality Gate: RuboCop'
85
+ puts '=' * 60
86
+ results[:rubocop] = system('bundle exec rubocop') ? :pass : :fail
87
+
88
+ puts "\n#{'=' * 60}"
89
+ puts 'Quality Gate: Flog Complexity'
90
+ puts '=' * 60
91
+ results[:flog] = system('bundle exec rake flog_check') ? :pass : :fail
92
+
93
+ puts "\n#{'=' * 60}"
94
+ puts 'Quality Summary'
95
+ puts '=' * 60
96
+ results.each do |gate, status|
97
+ icon = status == :pass ? 'PASS' : 'FAIL'
98
+ puts " [#{icon}] #{gate}"
99
+ end
100
+ puts '=' * 60
101
+
102
+ abort "\nQuality gate failed" if results.values.any?(:fail)
103
+ puts "\nAll quality gates passed."
104
+ end
105
+
106
+ namespace :docs do
107
+ desc 'Build MkDocs documentation'
108
+ task :build do
109
+ sh 'mkdocs build'
110
+ end
111
+
112
+ desc 'Serve MkDocs documentation locally on http://localhost:8000'
113
+ task :serve do
114
+ sh 'mkdocs serve'
115
+ end
116
+ end
@@ -4,6 +4,6 @@ module RobotLab
4
4
  # RactorPool (not Ractor) avoids shadowing Ruby's built-in Ractor class
5
5
  # for all code within the RobotLab namespace.
6
6
  module RactorPool
7
- VERSION = "0.1.0"
7
+ VERSION = '0.2.1'
8
8
  end
9
9
  end
@@ -5,16 +5,16 @@
5
5
  # code inside the RobotLab namespace, breaking Ractor.new / Ractor.make_shareable
6
6
  # calls in RactorWorkerPool, RactorNetworkScheduler, etc.
7
7
 
8
- require "etc"
9
- require "ractor_queue"
10
- require "ractor/wrapper"
8
+ require 'etc'
9
+ require 'ractor_queue'
10
+ require 'ractor/wrapper'
11
11
 
12
- require_relative "ractor/version"
13
- require_relative "ractor_job"
14
- require_relative "ractor_boundary"
15
- require_relative "ractor_worker_pool"
16
- require_relative "ractor_memory_proxy"
17
- require_relative "ractor_network_scheduler"
12
+ require_relative 'ractor/version'
13
+ require_relative 'ractor_job'
14
+ require_relative 'ractor_boundary'
15
+ require_relative 'ractor_worker_pool'
16
+ require_relative 'ractor_memory_proxy'
17
+ require_relative 'ractor_network_scheduler'
18
18
 
19
19
  # Extend the RobotLab module with Ractor pool lifecycle methods.
20
20
  # Once robot_lab removes its own copies and adds robot_lab-ractor as a
@@ -23,8 +23,11 @@ module RobotLab
23
23
  class << self
24
24
  def ractor_pool
25
25
  @ractor_pool ||= begin
26
- size = respond_to?(:config) && config.respond_to?(:ractor_pool_size) \
27
- ? (config.ractor_pool_size || :auto) : :auto
26
+ size = if respond_to?(:config) && config.respond_to?(:ractor_pool_size)
27
+ config.ractor_pool_size || :auto
28
+ else
29
+ :auto
30
+ end
28
31
  RactorWorkerPool.new(size: size)
29
32
  end
30
33
  end
@@ -35,3 +38,7 @@ module RobotLab
35
38
  end
36
39
  end
37
40
  end
41
+
42
+ if defined?(RobotLab) && RobotLab.respond_to?(:register_extension)
43
+ RobotLab.register_extension(:ractor, :ractor_extension_loaded)
44
+ end
@@ -35,7 +35,7 @@ module RobotLab
35
35
  end
36
36
 
37
37
  Ractor.make_shareable(result)
38
- rescue ::Ractor::IsolationError, ::Ractor::Error => e
38
+ rescue ::Ractor::Error => e
39
39
  raise RactorBoundaryError, "Cannot make value Ractor-shareable: #{e.message}"
40
40
  end
41
41
  end
@@ -31,9 +31,7 @@ module RobotLab
31
31
 
32
32
  # Returns the Ractor-shareable stub for use inside Ractors.
33
33
  # @return [Ractor::Wrapper stub]
34
- def stub
35
- @stub
36
- end
34
+ attr_reader :stub
37
35
 
38
36
  # Read a value from the proxied Memory.
39
37
  # @param key [Symbol]
@@ -61,7 +59,7 @@ module RobotLab
61
59
  def shutdown
62
60
  @wrapper.async_stop
63
61
  @wrapper.join
64
- rescue => e
62
+ rescue StandardError => e
65
63
  warn "RactorMemoryProxy shutdown error: #{e.message}"
66
64
  end
67
65
  end
@@ -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,38 @@ 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
+ robot.run(message).last_text_content.to_s.freeze
96
+ end
97
+
98
+ def self.wrap_error(err)
99
+ RobotLab::RactorJobError.new(
100
+ message: err.message.freeze,
101
+ backtrace: (err.backtrace || []).map(&:freeze).freeze
102
+ )
83
103
  end
84
104
 
85
105
  private
@@ -90,18 +110,16 @@ module RobotLab
90
110
  reply_q = RactorQueue.new(capacity: 1)
91
111
 
92
112
  job = RactorJob.new(
93
- id: SecureRandom.uuid.freeze,
94
- type: :robot,
95
- payload: RactorBoundary.freeze_deep({ spec: frozen_spec, message: frozen_message }),
113
+ id: SecureRandom.uuid.freeze,
114
+ type: :robot,
115
+ payload: RactorBoundary.freeze_deep({ spec: frozen_spec, message: frozen_message }),
96
116
  reply_queue: reply_q
97
117
  )
98
118
 
99
119
  @work_q.push(job)
100
120
  result = reply_q.pop
101
121
 
102
- if result.is_a?(RactorJobError)
103
- raise RobotLab::Error, "Robot '#{spec.name}' failed in Ractor: #{result.message}"
104
- end
122
+ raise RobotLab::Error, "Robot '#{spec.name}' failed in Ractor: #{result.message}" if result.is_a?(RactorJobError)
105
123
 
106
124
  result
107
125
  end
@@ -112,28 +130,24 @@ module RobotLab
112
130
  job = q.pop
113
131
  break if job.nil?
114
132
 
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
133
+ RobotLab::RactorNetworkScheduler.process_job(job)
135
134
  end
136
135
  end
137
136
  end
137
+
138
+ def partition_ready(remaining, completed)
139
+ remaining.partition do |entry|
140
+ deps = entry[:depends_on]
141
+ deps == :none || deps == :optional || Array(deps).all? { |d| completed.key?(d) }
142
+ end
143
+ end
144
+
145
+ def dispatch_ready(ready, completed, message)
146
+ ready.map do |entry|
147
+ spec = entry[:spec]
148
+ msg = completed.values.last || message
149
+ Thread.new { [spec.name, execute_spec(spec, msg)] }.tap { |t| t.report_on_exception = false }
150
+ end
151
+ end
138
152
  end
139
153
  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.1
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,6 +64,7 @@ extra_rdoc_files: []
64
64
  files:
65
65
  - ".envrc"
66
66
  - ".github/workflows/deploy-github-pages.yml"
67
+ - ".rubocop.yml"
67
68
  - CHANGELOG.md
68
69
  - LICENSE.txt
69
70
  - README.md