robot_lab 0.0.9 → 0.0.12

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -0
  3. data/README.md +210 -1
  4. data/Rakefile +2 -1
  5. data/docs/api/core/result.md +123 -0
  6. data/docs/api/core/robot.md +182 -0
  7. data/docs/api/errors.md +185 -0
  8. data/docs/guides/building-robots.md +125 -0
  9. data/docs/guides/creating-networks.md +21 -0
  10. data/docs/guides/index.md +10 -0
  11. data/docs/guides/knowledge.md +182 -0
  12. data/docs/guides/mcp-integration.md +106 -0
  13. data/docs/guides/memory.md +2 -0
  14. data/docs/guides/observability.md +486 -0
  15. data/docs/guides/ractor-parallelism.md +364 -0
  16. data/docs/superpowers/plans/2026-04-14-ractor-integration.md +1538 -0
  17. data/docs/superpowers/specs/2026-04-14-ractor-integration-design.md +258 -0
  18. data/examples/19_token_tracking.rb +128 -0
  19. data/examples/20_circuit_breaker.rb +153 -0
  20. data/examples/21_learning_loop.rb +164 -0
  21. data/examples/22_context_compression.rb +179 -0
  22. data/examples/23_convergence.rb +137 -0
  23. data/examples/24_structured_delegation.rb +150 -0
  24. data/examples/25_history_search/conversation.jsonl +30 -0
  25. data/examples/25_history_search.rb +136 -0
  26. data/examples/26_document_store/api_versioning_adr.md +52 -0
  27. data/examples/26_document_store/incident_postmortem.md +46 -0
  28. data/examples/26_document_store/postgres_runbook.md +49 -0
  29. data/examples/26_document_store/redis_caching_guide.md +48 -0
  30. data/examples/26_document_store/sidekiq_guide.md +51 -0
  31. data/examples/26_document_store.rb +147 -0
  32. data/examples/27_incident_response/incident_response.rb +244 -0
  33. data/examples/28_mcp_discovery.rb +112 -0
  34. data/examples/29_ractor_tools.rb +243 -0
  35. data/examples/30_ractor_network.rb +256 -0
  36. data/examples/README.md +136 -0
  37. data/examples/prompts/skill_with_mcp_test.md +9 -0
  38. data/examples/prompts/skill_with_robot_name_test.md +5 -0
  39. data/examples/prompts/skill_with_tools_test.md +6 -0
  40. data/lib/robot_lab/bus_poller.rb +149 -0
  41. data/lib/robot_lab/convergence.rb +69 -0
  42. data/lib/robot_lab/delegation_future.rb +93 -0
  43. data/lib/robot_lab/document_store.rb +155 -0
  44. data/lib/robot_lab/error.rb +25 -0
  45. data/lib/robot_lab/history_compressor.rb +205 -0
  46. data/lib/robot_lab/mcp/client.rb +17 -5
  47. data/lib/robot_lab/mcp/connection_poller.rb +187 -0
  48. data/lib/robot_lab/mcp/server.rb +7 -2
  49. data/lib/robot_lab/mcp/server_discovery.rb +110 -0
  50. data/lib/robot_lab/mcp/transports/stdio.rb +6 -0
  51. data/lib/robot_lab/memory.rb +103 -6
  52. data/lib/robot_lab/network.rb +44 -9
  53. data/lib/robot_lab/ractor_boundary.rb +42 -0
  54. data/lib/robot_lab/ractor_job.rb +37 -0
  55. data/lib/robot_lab/ractor_memory_proxy.rb +85 -0
  56. data/lib/robot_lab/ractor_network_scheduler.rb +154 -0
  57. data/lib/robot_lab/ractor_worker_pool.rb +117 -0
  58. data/lib/robot_lab/robot/bus_messaging.rb +43 -65
  59. data/lib/robot_lab/robot/history_search.rb +69 -0
  60. data/lib/robot_lab/robot.rb +228 -11
  61. data/lib/robot_lab/robot_result.rb +24 -5
  62. data/lib/robot_lab/run_config.rb +1 -1
  63. data/lib/robot_lab/text_analysis.rb +103 -0
  64. data/lib/robot_lab/tool.rb +42 -3
  65. data/lib/robot_lab/tool_config.rb +1 -1
  66. data/lib/robot_lab/version.rb +1 -1
  67. data/lib/robot_lab/waiter.rb +49 -29
  68. data/lib/robot_lab.rb +25 -0
  69. data/mkdocs.yml +1 -0
  70. metadata +72 -2
@@ -1,28 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RobotLab
4
- # Thread-safe waiter for blocking get operations on Memory
4
+ # Thread-safe waiter for blocking get operations on Memory.
5
5
  #
6
- # Waiter provides a condition variable wrapper that allows one thread
7
- # to wait for a value that will be provided by another thread.
6
+ # Uses an IO.pipe pair instead of ConditionVariable so that
7
+ # IO.select integrates with the Async fiber scheduler hook.
8
+ # ConditionVariable#wait can block the event loop in Async
9
+ # contexts; IO.select yields to the scheduler correctly.
8
10
  #
9
- # @example Basic usage
10
- # waiter = Waiter.new
11
- #
12
- # # In thread A (waiting)
13
- # value = waiter.wait(timeout: 30)
14
- #
15
- # # In thread B (signaling)
16
- # waiter.signal("the value")
11
+ # Multiple threads may wait on the same Waiter instance.
12
+ # signal() writes one byte per waiting thread so every
13
+ # blocked IO.select wakes exactly once.
17
14
  #
18
15
  # @api private
19
16
  class Waiter
20
17
  # Creates a new Waiter instance.
21
18
  def initialize
22
- @mutex = Mutex.new
23
- @condition = ConditionVariable.new
24
- @value = nil
25
- @signaled = false
19
+ @read_io, @write_io = IO.pipe
20
+ @mutex = Mutex.new
21
+ @value = nil
22
+ @signaled = false
23
+ @waiter_count = 0
26
24
  end
27
25
 
28
26
  # Wait for a value to be signaled.
@@ -34,32 +32,44 @@ module RobotLab
34
32
  @mutex.synchronize do
35
33
  return @value if @signaled
36
34
 
37
- if timeout
38
- deadline = Time.now + timeout
39
- until @signaled
40
- remaining = deadline - Time.now
41
- return :timeout if remaining <= 0
42
- @condition.wait(@mutex, remaining)
43
- end
44
- @value
45
- else
46
- @condition.wait(@mutex) until @signaled
35
+ @waiter_count += 1
36
+ end
37
+
38
+ begin
39
+ ready = IO.select([@read_io], nil, nil, timeout)
40
+
41
+ @mutex.synchronize do
42
+ @waiter_count -= 1
43
+ return :timeout unless ready
44
+
45
+ @read_io.read_nonblock(1) rescue nil # drain one wake byte
47
46
  @value
48
47
  end
48
+ rescue IOError
49
+ @mutex.synchronize { @waiter_count -= 1 }
50
+ :timeout
49
51
  end
50
52
  end
51
53
 
52
- # Signal a value to waiting threads.
54
+ # Signal a value to all waiting threads.
53
55
  #
54
56
  # @param value [Object] the value to signal
55
57
  # @return [void]
56
58
  #
57
59
  def signal(value)
58
- @mutex.synchronize do
59
- @value = value
60
+ count = @mutex.synchronize do
61
+ @value = value
60
62
  @signaled = true
61
- @condition.broadcast
63
+ @waiter_count
62
64
  end
65
+
66
+ # Write one byte per waiting thread (min 1 to handle the race
67
+ # where a thread passed the @signaled check but hasn't entered
68
+ # IO.select yet — its IO.select will return immediately).
69
+ bytes = [count, 1].max
70
+ @write_io.write_nonblock("." * bytes) rescue nil
71
+ rescue IOError
72
+ # pipe already closed — signal is a no-op
63
73
  end
64
74
 
65
75
  # Check if this waiter has been signaled.
@@ -69,5 +79,15 @@ module RobotLab
69
79
  def signaled?
70
80
  @mutex.synchronize { @signaled }
71
81
  end
82
+
83
+ # Release the pipe file descriptors.
84
+ # Should be called after wait returns.
85
+ #
86
+ # @return [void]
87
+ #
88
+ def close
89
+ @read_io.close rescue nil
90
+ @write_io.close rescue nil
91
+ end
72
92
  end
73
93
  end
data/lib/robot_lab.rb CHANGED
@@ -71,6 +71,7 @@ loader.setup
71
71
  require_relative 'robot_lab/error'
72
72
  require_relative 'robot_lab/message'
73
73
  require_relative 'robot_lab/memory'
74
+ require_relative 'robot_lab/ractor_job'
74
75
 
75
76
  # Eager load everything in Rails or when explicitly requested.
76
77
  # Otherwise Zeitwerk's lazy autoloading keeps boot fast.
@@ -218,6 +219,30 @@ module RobotLab
218
219
  def create_memory(data: {}, enable_cache: true, **options)
219
220
  Memory.new(data: data, enable_cache: enable_cache, **options)
220
221
  end
222
+
223
+
224
+ # Returns the shared RactorWorkerPool, lazily initialized.
225
+ #
226
+ # Pool size is determined by RobotLab.config.ractor_pool_size or
227
+ # defaults to Etc.nprocessors (:auto). The pool lives for the lifetime
228
+ # of the process. Call RobotLab.shutdown_ractor_pool to drain and
229
+ # close it explicitly.
230
+ #
231
+ # @return [RactorWorkerPool]
232
+ def ractor_pool
233
+ @ractor_pool ||= begin
234
+ size = config.respond_to?(:ractor_pool_size) ? (config.ractor_pool_size || :auto) : :auto
235
+ RactorWorkerPool.new(size: size)
236
+ end
237
+ end
238
+
239
+ # Shut down the shared Ractor worker pool, draining in-flight jobs.
240
+ #
241
+ # @return [void]
242
+ def shutdown_ractor_pool
243
+ @ractor_pool&.shutdown
244
+ @ractor_pool = nil
245
+ end
221
246
  end
222
247
  end
223
248
 
data/mkdocs.yml CHANGED
@@ -170,6 +170,7 @@ nav:
170
170
  - Streaming Responses: guides/streaming.md
171
171
  - Memory System: guides/memory.md
172
172
  - Rails Integration: guides/rails-integration.md
173
+ - Ractor Parallelism: guides/ractor-parallelism.md
173
174
  - API Reference:
174
175
  - api/index.md
175
176
  - Core Classes:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: robot_lab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
@@ -163,6 +163,34 @@ dependencies:
163
163
  - - "~>"
164
164
  - !ruby/object:Gem::Version
165
165
  version: 0.3.0
166
+ - !ruby/object:Gem::Dependency
167
+ name: ractor_queue
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ type: :runtime
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: ractor-wrapper
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ type: :runtime
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
166
194
  - !ruby/object:Gem::Dependency
167
195
  name: async-http
168
196
  requirement: !ruby/object:Gem::Requirement
@@ -216,9 +244,11 @@ files:
216
244
  - docs/api/core/index.md
217
245
  - docs/api/core/memory.md
218
246
  - docs/api/core/network.md
247
+ - docs/api/core/result.md
219
248
  - docs/api/core/robot.md
220
249
  - docs/api/core/state.md
221
250
  - docs/api/core/tool.md
251
+ - docs/api/errors.md
222
252
  - docs/api/index.md
223
253
  - docs/api/mcp/client.md
224
254
  - docs/api/mcp/index.md
@@ -254,12 +284,17 @@ files:
254
284
  - docs/guides/building-robots.md
255
285
  - docs/guides/creating-networks.md
256
286
  - docs/guides/index.md
287
+ - docs/guides/knowledge.md
257
288
  - docs/guides/mcp-integration.md
258
289
  - docs/guides/memory.md
290
+ - docs/guides/observability.md
291
+ - docs/guides/ractor-parallelism.md
259
292
  - docs/guides/rails-integration.md
260
293
  - docs/guides/streaming.md
261
294
  - docs/guides/using-tools.md
262
295
  - docs/index.md
296
+ - docs/superpowers/plans/2026-04-14-ractor-integration.md
297
+ - docs/superpowers/specs/2026-04-14-ractor-integration-design.md
263
298
  - examples/01_simple_robot.rb
264
299
  - examples/02_tools.rb
265
300
  - examples/03_network.rb
@@ -343,6 +378,24 @@ files:
343
378
  - examples/18_rails/config/initializers/robot_lab.rb
344
379
  - examples/18_rails/config/routes.rb
345
380
  - examples/18_rails/db/migrate/001_create_robot_lab_tables.rb
381
+ - examples/19_token_tracking.rb
382
+ - examples/20_circuit_breaker.rb
383
+ - examples/21_learning_loop.rb
384
+ - examples/22_context_compression.rb
385
+ - examples/23_convergence.rb
386
+ - examples/24_structured_delegation.rb
387
+ - examples/25_history_search.rb
388
+ - examples/25_history_search/conversation.jsonl
389
+ - examples/26_document_store.rb
390
+ - examples/26_document_store/api_versioning_adr.md
391
+ - examples/26_document_store/incident_postmortem.md
392
+ - examples/26_document_store/postgres_runbook.md
393
+ - examples/26_document_store/redis_caching_guide.md
394
+ - examples/26_document_store/sidekiq_guide.md
395
+ - examples/27_incident_response/incident_response.rb
396
+ - examples/28_mcp_discovery.rb
397
+ - examples/29_ractor_tools.rb
398
+ - examples/30_ractor_network.rb
346
399
  - examples/README.md
347
400
  - examples/prompts/assistant.md
348
401
  - examples/prompts/audit_trail.md
@@ -382,7 +435,10 @@ files:
382
435
  - examples/prompts/skill_nested_test.md
383
436
  - examples/prompts/skill_refs_main_test.md
384
437
  - examples/prompts/skill_self_ref_test.md
438
+ - examples/prompts/skill_with_mcp_test.md
385
439
  - examples/prompts/skill_with_params_test.md
440
+ - examples/prompts/skill_with_robot_name_test.md
441
+ - examples/prompts/skill_with_tools_test.md
386
442
  - examples/prompts/sre_compliance.md
387
443
  - examples/prompts/structured_output.md
388
444
  - examples/prompts/synthesizer.md
@@ -401,11 +457,18 @@ files:
401
457
  - lib/generators/robot_lab/templates/thread_model.rb.tt
402
458
  - lib/robot_lab.rb
403
459
  - lib/robot_lab/ask_user.rb
460
+ - lib/robot_lab/bus_poller.rb
404
461
  - lib/robot_lab/config.rb
405
462
  - lib/robot_lab/config/defaults.yml
463
+ - lib/robot_lab/convergence.rb
464
+ - lib/robot_lab/delegation_future.rb
465
+ - lib/robot_lab/document_store.rb
406
466
  - lib/robot_lab/error.rb
467
+ - lib/robot_lab/history_compressor.rb
407
468
  - lib/robot_lab/mcp/client.rb
469
+ - lib/robot_lab/mcp/connection_poller.rb
408
470
  - lib/robot_lab/mcp/server.rb
471
+ - lib/robot_lab/mcp/server_discovery.rb
409
472
  - lib/robot_lab/mcp/transports/base.rb
410
473
  - lib/robot_lab/mcp/transports/sse.rb
411
474
  - lib/robot_lab/mcp/transports/stdio.rb
@@ -415,11 +478,17 @@ files:
415
478
  - lib/robot_lab/memory_change.rb
416
479
  - lib/robot_lab/message.rb
417
480
  - lib/robot_lab/network.rb
481
+ - lib/robot_lab/ractor_boundary.rb
482
+ - lib/robot_lab/ractor_job.rb
483
+ - lib/robot_lab/ractor_memory_proxy.rb
484
+ - lib/robot_lab/ractor_network_scheduler.rb
485
+ - lib/robot_lab/ractor_worker_pool.rb
418
486
  - lib/robot_lab/rails_integration/engine.rb
419
487
  - lib/robot_lab/rails_integration/railtie.rb
420
488
  - lib/robot_lab/rails_integration/turbo_stream_callbacks.rb
421
489
  - lib/robot_lab/robot.rb
422
490
  - lib/robot_lab/robot/bus_messaging.rb
491
+ - lib/robot_lab/robot/history_search.rb
423
492
  - lib/robot_lab/robot/mcp_management.rb
424
493
  - lib/robot_lab/robot/template_rendering.rb
425
494
  - lib/robot_lab/robot_message.rb
@@ -430,6 +499,7 @@ files:
430
499
  - lib/robot_lab/streaming/events.rb
431
500
  - lib/robot_lab/streaming/sequence_counter.rb
432
501
  - lib/robot_lab/task.rb
502
+ - lib/robot_lab/text_analysis.rb
433
503
  - lib/robot_lab/tool.rb
434
504
  - lib/robot_lab/tool_config.rb
435
505
  - lib/robot_lab/tool_manifest.rb
@@ -464,7 +534,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
464
534
  - !ruby/object:Gem::Version
465
535
  version: '0'
466
536
  requirements: []
467
- rubygems_version: 4.0.7
537
+ rubygems_version: 4.0.10
468
538
  specification_version: 4
469
539
  summary: Ruby framework for building and orchestrating multi-robot LLM workflows
470
540
  test_files: []