robot_lab 0.2.6 → 0.2.7

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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +124 -64
  3. data/docs/api/core/index.md +41 -15
  4. data/docs/api/core/memory.md +247 -29
  5. data/docs/api/core/network.md +255 -33
  6. data/docs/api/core/result.md +120 -32
  7. data/docs/api/core/robot.md +551 -61
  8. data/docs/api/core/state.md +87 -197
  9. data/docs/api/core/tool.md +165 -20
  10. data/docs/api/errors.md +110 -17
  11. data/docs/api/hooks.md +469 -0
  12. data/docs/api/index.md +80 -7
  13. data/docs/api/mcp/client.md +129 -35
  14. data/docs/api/mcp/index.md +164 -23
  15. data/docs/api/mcp/server.md +27 -3
  16. data/docs/api/mcp/transports.md +94 -22
  17. data/docs/api/messages/index.md +26 -3
  18. data/docs/api/messages/text-message.md +33 -11
  19. data/docs/api/messages/tool-call-message.md +27 -4
  20. data/docs/api/messages/tool-result-message.md +23 -4
  21. data/docs/api/messages/user-message.md +45 -8
  22. data/docs/api/skills.md +519 -0
  23. data/docs/api/streaming/context.md +28 -5
  24. data/docs/api/streaming/index.md +57 -11
  25. data/docs/api/support.md +846 -0
  26. data/docs/architecture/core-concepts.md +79 -31
  27. data/docs/architecture/index.md +86 -11
  28. data/docs/architecture/message-flow.md +66 -29
  29. data/docs/architecture/network-orchestration.md +145 -38
  30. data/docs/architecture/robot-execution.md +172 -90
  31. data/docs/architecture/state-management.md +31 -12
  32. data/docs/concepts.md +176 -21
  33. data/docs/examples/basic-chat.md +72 -19
  34. data/docs/examples/index.md +117 -31
  35. data/docs/examples/mcp-server.md +154 -45
  36. data/docs/examples/multi-robot-network.md +91 -21
  37. data/docs/examples/tool-usage.md +104 -37
  38. data/docs/getting-started/configuration.md +284 -97
  39. data/docs/getting-started/installation.md +53 -41
  40. data/docs/getting-started/quick-start.md +51 -6
  41. data/docs/guides/building-robots.md +258 -50
  42. data/docs/guides/creating-networks.md +214 -30
  43. data/docs/guides/hooks.md +141 -54
  44. data/docs/guides/knowledge.md +35 -4
  45. data/docs/guides/mcp-integration.md +211 -44
  46. data/docs/guides/memory.md +103 -12
  47. data/docs/guides/observability.md +95 -47
  48. data/docs/guides/streaming.md +184 -125
  49. data/docs/guides/using-tools.md +237 -17
  50. data/docs/index.md +36 -4
  51. data/examples/01_simple_robot.rb +2 -2
  52. data/examples/02_tools.rb +14 -4
  53. data/examples/03_network.rb +12 -7
  54. data/examples/04_mcp.rb +11 -4
  55. data/examples/05_streaming.rb +8 -5
  56. data/examples/06_prompt_templates.rb +13 -9
  57. data/examples/07_network_memory.rb +5 -5
  58. data/examples/08_llm_config.rb +20 -15
  59. data/examples/09_chaining.rb +4 -4
  60. data/examples/11_network_introspection.rb +4 -4
  61. data/examples/12_message_bus.rb +2 -2
  62. data/examples/13_spawn.rb +2 -2
  63. data/examples/14_rusty_circuit/README.md +1 -0
  64. data/examples/14_rusty_circuit/comic.rb +7 -3
  65. data/examples/14_rusty_circuit/display.rb +14 -0
  66. data/examples/14_rusty_circuit/heckler.rb +8 -6
  67. data/examples/14_rusty_circuit/open_mic.rb +17 -6
  68. data/examples/14_rusty_circuit/scout.rb +17 -10
  69. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +14 -10
  70. data/examples/15_memory_network_and_bus/linux_writer.rb +2 -2
  71. data/examples/15_memory_network_and_bus/os_editor.rb +3 -1
  72. data/examples/15_memory_network_and_bus/os_writer.rb +4 -1
  73. data/examples/16_writers_room/writer.rb +22 -22
  74. data/examples/16_writers_room/writers_room.rb +2 -0
  75. data/examples/17_skills.rb +14 -13
  76. data/examples/18_rails/README.md +20 -1
  77. data/examples/18_rails/app/controllers/chat_controller.rb +5 -1
  78. data/examples/18_rails/app/jobs/robot_run_job.rb +11 -5
  79. data/examples/18_rails/app/robots/chat_robot.rb +11 -0
  80. data/examples/18_rails/config/initializers/robot_lab.rb +8 -0
  81. data/examples/19_token_tracking.rb +25 -9
  82. data/examples/20_circuit_breaker.rb +10 -7
  83. data/examples/21_learning_loop.rb +42 -16
  84. data/examples/22_context_compression.rb +23 -23
  85. data/examples/23_convergence.rb +24 -17
  86. data/examples/24_structured_delegation.rb +13 -8
  87. data/examples/25_history_search.rb +12 -8
  88. data/examples/27_incident_response/incident_response.rb +31 -13
  89. data/examples/28_mcp_discovery.rb +17 -13
  90. data/examples/29_ractor_tools.rb +4 -2
  91. data/examples/30_ractor_network.rb +22 -17
  92. data/examples/31_launch_assessment.rb +20 -9
  93. data/examples/32_newsletter_reader.rb +7 -2
  94. data/examples/33_stock_predictor.rb +34 -13
  95. data/examples/34_agentskills.rb +7 -3
  96. data/examples/35_hooks.rb +18 -8
  97. data/examples/README.md +199 -45
  98. data/examples/common.rb +79 -11
  99. data/examples/xyzzy.rb +8 -1
  100. data/lib/robot_lab/config.rb +10 -5
  101. data/lib/robot_lab/names.rb +402 -0
  102. data/lib/robot_lab/robot/agent_skill_matching.rb +1 -3
  103. data/lib/robot_lab/robot/bus_messaging.rb +16 -8
  104. data/lib/robot_lab/robot/template_rendering.rb +16 -3
  105. data/lib/robot_lab/robot.rb +23 -2
  106. data/lib/robot_lab/version.rb +1 -1
  107. data/lib/robot_lab.rb +21 -15
  108. data/mkdocs.yml +6 -1
  109. metadata +7 -2
@@ -37,36 +37,47 @@ RobotLab automatically installs these core dependencies:
37
37
  |-----|---------|
38
38
  | `ruby_llm` (~> 1.12) | LLM provider integrations (Anthropic, OpenAI, Gemini, etc.) |
39
39
  | `prompt_manager` (~> 1.0) | Template-based prompt management with YAML front matter |
40
- | `simple_flow` (~> 0.3) | Pipeline workflow execution for networks |
40
+ | `simple_flow` (~> 0.4) | Pipeline workflow execution for networks |
41
41
  | `myway_config` (~> 0.1) | Layered configuration (defaults, env vars, config files) |
42
- | `ruby_llm-mcp` | Model Context Protocol client for external tool servers |
43
- | `ruby_llm-schema` | Schema validation for structured outputs |
44
- | `ruby_llm-semantic_cache` | Semantic caching for LLM responses |
42
+ | `ruby_llm-mcp` (~> 1.0) | Model Context Protocol client for external tool servers |
43
+ | `ruby_llm-schema` (~> 0.3) | Schema validation for structured outputs |
44
+ | `ruby_llm-semantic_cache` (~> 0.1) | Semantic caching for LLM responses |
45
45
  | `zeitwerk` (~> 2.6) | Autoloading and eager loading |
46
46
  | `async` (~> 2.0) | Fiber-based concurrency |
47
+ | `async-http` (~> 0.60) | MCP SSE and streamable-HTTP transports |
48
+ | `async-websocket` (~> 0.30) | MCP WebSocket transport |
49
+ | `typed_bus` (~> 0.0.1) | Typed message bus for robot-to-robot messaging |
50
+ | `ractor_queue` (~> 0.2) | Queue used by the Network bus poller |
47
51
 
48
- ### Optional Dependencies
49
-
50
- For specific features, you may need additional gems:
51
-
52
- === "MCP WebSocket Transport"
52
+ > [!NOTE]
53
+ > `async-http` and `async-websocket` are hard runtime dependencies, not optional
54
+ > extras they install with the gem whether or not you use MCP.
53
55
 
54
- ```ruby
55
- gem "async-websocket"
56
- ```
56
+ ### Optional Dependencies
57
57
 
58
- === "MCP HTTP Transport"
58
+ Several features are gated behind gems the core gem does **not** install. Add
59
+ whichever you need to your own `Gemfile`:
59
60
 
60
- ```ruby
61
- gem "async-http"
62
- ```
61
+ | Gem | Enables | Without it |
62
+ |-----|---------|------------|
63
+ | `classifier` (~> 2.3) | `robot.compress_history`, `RobotLab::Convergence` | `RobotLab::DependencyError` |
64
+ | `robot_lab-document_store` | `memory.store_document` / `search_documents` / `document_keys` / `delete_document`, and `AgentSkill` catalogs | `RobotLab::DependencyError` (skill catalogs raise `LoadError`) |
65
+ | `redis` | Redis-backed `Memory` | `Memory#redis?` is `false` and the store silently stays in-process |
66
+ | `robot_lab-ractor` | Ractor-parallel tool execution and `Network` ractor scheduling | tools run inline; a network run with `parallel_mode: :ractor` raises `RobotLab::DependencyError` |
63
67
 
64
- === "Rails Integration"
68
+ ```ruby
69
+ # Gemfile
70
+ gem "classifier", "~> 2.3"
71
+ gem "robot_lab-document_store"
72
+ gem "redis"
73
+ gem "robot_lab-ractor"
74
+ ```
65
75
 
66
- ```ruby
67
- # Rails is detected automatically
68
- gem "rails", ">= 7.0"
69
- ```
76
+ > [!NOTE]
77
+ > Several capabilities advertised on the [home page](../index.md) — embedding-based
78
+ > document retrieval, runtime skill matching, and CPU parallelism — depend on the
79
+ > gems above. They are genuinely optional, but the feature is unavailable until the
80
+ > gem is installed.
70
81
 
71
82
  ## Verify Installation
72
83
 
@@ -84,33 +95,33 @@ Run it:
84
95
 
85
96
  ```bash
86
97
  ruby test_robot_lab.rb
87
- # => RobotLab version: 0.1.0
98
+ # => RobotLab version: 0.2.6
88
99
  # => Installation successful!
89
100
  ```
90
101
 
91
102
  ## Rails Installation
92
103
 
93
- For Rails applications, use the install generator:
94
-
95
- ```bash
96
- rails generate robot_lab:install
97
- ```
98
-
99
- This creates:
104
+ > [!IMPORTANT]
105
+ > The core `robot_lab` gem ships **no Rails Engine, no Railtie, and no generators**.
106
+ > Rails integration lives in the separate
107
+ > [robot_lab-rails](https://github.com/MadBomber/robot_lab-rails) gem.
100
108
 
101
- - `config/initializers/robot_lab.rb` - Configuration file
102
- - `db/migrate/*_create_robot_lab_tables.rb` - Database migrations
103
- - `app/models/robot_lab_thread.rb` - Thread model
104
- - `app/models/robot_lab_result.rb` - Result model
105
- - `app/robots/` - Directory for robot definitions
106
- - `app/tools/` - Directory for tool definitions
109
+ Core RobotLab only performs two bare `defined?(::Rails)` checks: it uses
110
+ `Rails.logger` as the default logger, and it resolves the template path to
111
+ `app/prompts` when `Rails.root` is present. Nothing else is Rails-aware.
107
112
 
108
- Then run migrations:
113
+ For generators, an `ActiveJob` base class, and Turbo Stream broadcasting, add the
114
+ extension gem:
109
115
 
110
- ```bash
111
- rails db:migrate
116
+ ```ruby
117
+ # Gemfile
118
+ gem "robot_lab"
119
+ gem "robot_lab-rails"
112
120
  ```
113
121
 
122
+ Then follow that gem's own installation instructions for its generators and
123
+ migrations.
124
+
114
125
  ## Environment Setup
115
126
 
116
127
  RobotLab uses a layered configuration system (see [Configuration](configuration.md) for full details). The simplest way to get started is with environment variables:
@@ -165,11 +176,12 @@ gem install robot_lab --verbose
165
176
 
166
177
  ### Missing Dependencies
167
178
 
168
- If you see "LoadError" for optional gems:
179
+ If you see a `LoadError` (or a `RobotLab::DependencyError`) naming a gem RobotLab
180
+ does not depend on, add it yourself:
169
181
 
170
182
  ```bash
171
- # Install the specific gem mentioned in the error
172
- bundle add async-websocket
183
+ # e.g. compress_history / Convergence need the classifier gem
184
+ bundle add classifier
173
185
  ```
174
186
 
175
187
  ### API Key Issues
@@ -13,11 +13,15 @@ export ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY="sk-ant-..."
13
13
  Or create a config file at `./config/robot_lab.yml`:
14
14
 
15
15
  ```yaml
16
- defaults:
17
- ruby_llm:
18
- anthropic_api_key: <%= ENV['ANTHROPIC_API_KEY'] %>
16
+ ruby_llm:
17
+ anthropic_api_key: <%= ENV['ANTHROPIC_API_KEY'] %>
19
18
  ```
20
19
 
20
+ > [!IMPORTANT]
21
+ > Config files are **flat** — put the keys at the top level. Wrapping them in a
22
+ > `defaults:` key (as the gem's own bundled `defaults.yml` does) makes the whole
23
+ > file silently ignored.
24
+
21
25
  See [Configuration](configuration.md) for all configuration options.
22
26
 
23
27
  ## Step 2: Create a Robot
@@ -140,12 +144,30 @@ assistant = RobotLab.build(
140
144
  local_tools: [CurrentTime]
141
145
  )
142
146
 
143
- result = assistant.run("What time is it right now?")
147
+ # tools: :inherit is REQUIRED -- run() sends no tools without it
148
+ result = assistant.run("What time is it right now?", tools: :inherit)
144
149
  puts result.last_text_content
145
150
  ```
146
151
 
147
152
  Tools are passed to the robot via the `local_tools:` keyword argument as an array of `RubyLLM::Tool` subclasses.
148
153
 
154
+ > [!WARNING]
155
+ > **Attaching a tool is not the same as sending it.** `Robot#run` defaults to
156
+ > `tools: :none`, which means "send zero tools this turn". Without
157
+ > `tools: :inherit` on the run, the example above builds fine, calls the LLM
158
+ > fine, and the model simply never sees `CurrentTime` — it will guess or say it
159
+ > cannot check the time. The same applies to MCP: pass
160
+ > `mcp: :inherit, tools: :inherit` to connect MCP servers and expose their tools.
161
+ >
162
+ > Do **not** try to fix this by passing `tools: :inherit` to `RobotLab.build` for
163
+ > a standalone robot like this one — at build time it resolves against the global
164
+ > `:none` and yields an allowlist matching nothing, suppressing the tools even
165
+ > when the run asks for them. Put it on the `run` call.
166
+ >
167
+ > (The one place build-time `:inherit` *is* correct is a robot in a network, where
168
+ > it opts into the network `config:`'s list — see
169
+ > [Creating Networks](../guides/creating-networks.md).)
170
+
149
171
  ## Method Chaining
150
172
 
151
173
  Robots support a chaining API for runtime adjustments:
@@ -162,7 +184,20 @@ result = robot
162
184
  puts result.last_text_content
163
185
  ```
164
186
 
165
- Available chaining methods include `with_instructions`, `with_temperature`, `with_model`, `with_max_tokens`, `with_top_p`, `with_tools`, and more.
187
+ The complete set of chaining methods is `with_context`, `with_headers`,
188
+ `with_instructions`, `with_model`, `with_params`, `with_schema`,
189
+ `with_temperature`, `with_thinking`, `with_tool`, `with_tools`, plus RobotLab's
190
+ own `with_template` and `with_bus`.
191
+
192
+ > [!NOTE]
193
+ > There is no `with_max_tokens`, `with_top_p`, `with_top_k`, `with_stop`,
194
+ > `with_presence_penalty`, or `with_frequency_penalty` — those raise
195
+ > `NoMethodError`. Set them as constructor keyword arguments
196
+ > (`RobotLab.build(..., max_tokens: 2000)`) or through `with_params`:
197
+ >
198
+ > ```ruby
199
+ > robot.with_params(max_tokens: 2000, top_p: 0.3).run("...")
200
+ > ```
166
201
 
167
202
  ## Multi-Robot Network
168
203
 
@@ -205,12 +240,22 @@ if result.value.is_a?(RobotLab::RobotResult)
205
240
  puts result.value.last_text_content
206
241
  end
207
242
 
208
- # Access intermediate results by task name
243
+ # Access intermediate results by ROBOT name (see note below)
209
244
  if result.context[:analyst]
210
245
  puts "\nAnalysis: #{result.context[:analyst].last_text_content}"
211
246
  end
212
247
  ```
213
248
 
249
+ > [!IMPORTANT]
250
+ > `result.context` is keyed by the **robot's** `name:`, not by the task name.
251
+ > Internally each robot writes `result.with_context(@name.to_sym, robot_result)`.
252
+ > The lookup above works only because the robot is named `"analyst"` *and* its
253
+ > task is named `:analyst`. If you register a robot under a different task name —
254
+ > `task :first_pass, analyst` — the result is still at
255
+ > `result.context[:analyst]`, not `result.context[:first_pass]`. Keeping the two
256
+ > names identical, as in this example, is the simplest way to avoid the
257
+ > surprise.
258
+
214
259
  ### Network Task Dependencies
215
260
 
216
261
  Tasks declare their dependencies to control execution order: