robot_lab 0.0.1 → 0.0.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.
Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +9 -9
  3. data/.irbrc +6 -0
  4. data/CHANGELOG.md +140 -0
  5. data/README.md +263 -48
  6. data/Rakefile +71 -1
  7. data/docs/api/core/index.md +53 -46
  8. data/docs/api/core/memory.md +200 -154
  9. data/docs/api/core/network.md +13 -3
  10. data/docs/api/core/robot.md +490 -130
  11. data/docs/api/core/state.md +55 -73
  12. data/docs/api/core/tool.md +205 -209
  13. data/docs/api/index.md +7 -28
  14. data/docs/api/mcp/client.md +119 -48
  15. data/docs/api/mcp/index.md +75 -60
  16. data/docs/api/mcp/server.md +120 -136
  17. data/docs/api/mcp/transports.md +172 -184
  18. data/docs/api/messages/index.md +35 -20
  19. data/docs/api/messages/text-message.md +67 -21
  20. data/docs/api/messages/tool-call-message.md +80 -41
  21. data/docs/api/messages/tool-result-message.md +119 -50
  22. data/docs/api/messages/user-message.md +48 -24
  23. data/docs/api/streaming/context.md +157 -74
  24. data/docs/api/streaming/events.md +114 -166
  25. data/docs/api/streaming/index.md +74 -72
  26. data/docs/architecture/core-concepts.md +360 -116
  27. data/docs/architecture/index.md +97 -59
  28. data/docs/architecture/message-flow.md +138 -129
  29. data/docs/architecture/network-orchestration.md +197 -50
  30. data/docs/architecture/robot-execution.md +199 -146
  31. data/docs/architecture/state-management.md +255 -187
  32. data/docs/concepts.md +311 -49
  33. data/docs/examples/basic-chat.md +89 -77
  34. data/docs/examples/index.md +222 -47
  35. data/docs/examples/mcp-server.md +207 -203
  36. data/docs/examples/multi-robot-network.md +129 -35
  37. data/docs/examples/rails-application.md +159 -160
  38. data/docs/examples/tool-usage.md +295 -204
  39. data/docs/getting-started/configuration.md +347 -154
  40. data/docs/getting-started/index.md +1 -1
  41. data/docs/getting-started/installation.md +22 -13
  42. data/docs/getting-started/quick-start.md +166 -121
  43. data/docs/guides/building-robots.md +418 -212
  44. data/docs/guides/creating-networks.md +143 -24
  45. data/docs/guides/index.md +0 -5
  46. data/docs/guides/mcp-integration.md +152 -113
  47. data/docs/guides/memory.md +220 -164
  48. data/docs/guides/rails-integration.md +244 -162
  49. data/docs/guides/streaming.md +137 -187
  50. data/docs/guides/using-tools.md +259 -212
  51. data/docs/index.md +46 -41
  52. data/examples/01_simple_robot.rb +6 -9
  53. data/examples/02_tools.rb +6 -9
  54. data/examples/03_network.rb +19 -17
  55. data/examples/04_mcp.rb +5 -8
  56. data/examples/05_streaming.rb +5 -8
  57. data/examples/06_prompt_templates.rb +42 -37
  58. data/examples/07_network_memory.rb +13 -14
  59. data/examples/08_llm_config.rb +169 -0
  60. data/examples/09_chaining.rb +262 -0
  61. data/examples/10_memory.rb +331 -0
  62. data/examples/11_network_introspection.rb +253 -0
  63. data/examples/12_message_bus.rb +74 -0
  64. data/examples/13_spawn.rb +90 -0
  65. data/examples/14_rusty_circuit/comic.rb +143 -0
  66. data/examples/14_rusty_circuit/display.rb +203 -0
  67. data/examples/14_rusty_circuit/heckler.rb +63 -0
  68. data/examples/14_rusty_circuit/open_mic.rb +123 -0
  69. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
  70. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
  71. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
  72. data/examples/14_rusty_circuit/scout.rb +156 -0
  73. data/examples/14_rusty_circuit/scout_notes.md +89 -0
  74. data/examples/14_rusty_circuit/show.log +234 -0
  75. data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
  76. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
  77. data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
  78. data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
  79. data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
  80. data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
  81. data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
  82. data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
  83. data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
  84. data/examples/15_memory_network_and_bus/output/memory.json +13 -0
  85. data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
  86. data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
  87. data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
  88. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
  89. data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
  90. data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
  91. data/examples/16_writers_room/display.rb +158 -0
  92. data/examples/16_writers_room/output/.gitignore +2 -0
  93. data/examples/16_writers_room/output/opus_001.md +263 -0
  94. data/examples/16_writers_room/output/opus_001_notes.log +470 -0
  95. data/examples/16_writers_room/prompts/writer.md +37 -0
  96. data/examples/16_writers_room/room.rb +150 -0
  97. data/examples/16_writers_room/tools.rb +162 -0
  98. data/examples/16_writers_room/writer.rb +121 -0
  99. data/examples/16_writers_room/writers_room.rb +162 -0
  100. data/examples/README.md +197 -0
  101. data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
  102. data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
  103. data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
  104. data/examples/prompts/comedian.md +6 -0
  105. data/examples/prompts/comedy_critic.md +10 -0
  106. data/examples/prompts/configurable.md +9 -0
  107. data/examples/prompts/dispatcher.md +12 -0
  108. data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
  109. data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
  110. data/examples/prompts/frontmatter_mcp_test.md +9 -0
  111. data/examples/prompts/frontmatter_named_test.md +5 -0
  112. data/examples/prompts/frontmatter_tools_test.md +6 -0
  113. data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
  114. data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
  115. data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
  116. data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
  117. data/examples/prompts/llm_config_demo.md +20 -0
  118. data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
  119. data/examples/prompts/os_advocate.md +13 -0
  120. data/examples/prompts/os_chief.md +13 -0
  121. data/examples/prompts/os_editor.md +13 -0
  122. data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
  123. data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
  124. data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
  125. data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
  126. data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
  127. data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
  128. data/lib/robot_lab/ask_user.rb +75 -0
  129. data/lib/robot_lab/config/defaults.yml +121 -0
  130. data/lib/robot_lab/config.rb +183 -0
  131. data/lib/robot_lab/error.rb +6 -0
  132. data/lib/robot_lab/mcp/client.rb +1 -1
  133. data/lib/robot_lab/memory.rb +10 -34
  134. data/lib/robot_lab/network.rb +13 -20
  135. data/lib/robot_lab/robot/bus_messaging.rb +239 -0
  136. data/lib/robot_lab/robot/mcp_management.rb +88 -0
  137. data/lib/robot_lab/robot/template_rendering.rb +130 -0
  138. data/lib/robot_lab/robot.rb +240 -330
  139. data/lib/robot_lab/robot_message.rb +44 -0
  140. data/lib/robot_lab/robot_result.rb +1 -0
  141. data/lib/robot_lab/run_config.rb +184 -0
  142. data/lib/robot_lab/state_proxy.rb +2 -12
  143. data/lib/robot_lab/streaming/context.rb +1 -1
  144. data/lib/robot_lab/task.rb +8 -1
  145. data/lib/robot_lab/tool.rb +108 -172
  146. data/lib/robot_lab/tool_config.rb +1 -1
  147. data/lib/robot_lab/tool_manifest.rb +2 -18
  148. data/lib/robot_lab/utils.rb +39 -0
  149. data/lib/robot_lab/version.rb +1 -1
  150. data/lib/robot_lab.rb +89 -57
  151. data/mkdocs.yml +0 -11
  152. metadata +121 -135
  153. data/docs/api/adapters/anthropic.md +0 -121
  154. data/docs/api/adapters/gemini.md +0 -133
  155. data/docs/api/adapters/index.md +0 -104
  156. data/docs/api/adapters/openai.md +0 -134
  157. data/docs/api/history/active-record-adapter.md +0 -195
  158. data/docs/api/history/config.md +0 -191
  159. data/docs/api/history/index.md +0 -132
  160. data/docs/api/history/thread-manager.md +0 -144
  161. data/docs/guides/history.md +0 -359
  162. data/examples/prompts/assistant/user.txt.erb +0 -1
  163. data/examples/prompts/billing/user.txt.erb +0 -1
  164. data/examples/prompts/classifier/user.txt.erb +0 -1
  165. data/examples/prompts/entity_extractor/user.txt.erb +0 -3
  166. data/examples/prompts/escalation/user.txt.erb +0 -34
  167. data/examples/prompts/general/user.txt.erb +0 -1
  168. data/examples/prompts/github_assistant/user.txt.erb +0 -1
  169. data/examples/prompts/helper/user.txt.erb +0 -1
  170. data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
  171. data/examples/prompts/order_support/user.txt.erb +0 -22
  172. data/examples/prompts/product_support/user.txt.erb +0 -32
  173. data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
  174. data/examples/prompts/synthesizer/user.txt.erb +0 -15
  175. data/examples/prompts/technical/user.txt.erb +0 -1
  176. data/examples/prompts/triage/user.txt.erb +0 -17
  177. data/lib/robot_lab/adapters/anthropic.rb +0 -163
  178. data/lib/robot_lab/adapters/base.rb +0 -85
  179. data/lib/robot_lab/adapters/gemini.rb +0 -193
  180. data/lib/robot_lab/adapters/openai.rb +0 -159
  181. data/lib/robot_lab/adapters/registry.rb +0 -81
  182. data/lib/robot_lab/configuration.rb +0 -143
  183. data/lib/robot_lab/errors.rb +0 -70
  184. data/lib/robot_lab/history/active_record_adapter.rb +0 -146
  185. data/lib/robot_lab/history/config.rb +0 -115
  186. data/lib/robot_lab/history/thread_manager.rb +0 -93
  187. data/lib/robot_lab/robotic_model.rb +0 -324
@@ -1,226 +1,419 @@
1
1
  # Configuration
2
2
 
3
- RobotLab provides flexible configuration options at global, network, and robot levels.
3
+ RobotLab uses a layered configuration system powered by [MywayConfig](https://github.com/MadBomber/myway_config). Configuration is loaded automatically from multiple sources with no block-style `configure` method required.
4
4
 
5
- ## Global Configuration
5
+ ## How Configuration Works
6
6
 
7
- Configure RobotLab globally using the `configure` block:
7
+ Configuration values are loaded in priority order (lowest to highest):
8
+
9
+ 1. **Bundled defaults** -- `lib/robot_lab/config/defaults.yml` (shipped with the gem)
10
+ 2. **Environment-specific overrides** -- `development`, `test`, or `production` sections in defaults.yml
11
+ 3. **User config file** -- `~/.config/robot_lab/config.yml`
12
+ 4. **Project config file** -- `./config/robot_lab.yml`
13
+ 5. **Environment variables** -- `ROBOT_LAB_*` prefix
14
+ 6. **Runtime attributes** -- e.g., `RobotLab.config.logger = ...`
15
+
16
+ Higher-priority sources override lower-priority ones. You only need to set the values you want to change.
17
+
18
+ ## Accessing Configuration
19
+
20
+ Use `RobotLab.config` to access the configuration object:
8
21
 
9
22
  ```ruby
10
- RobotLab.configure do |config|
11
- # LLM Provider API Keys
12
- config.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
13
- config.openai_api_key = ENV["OPENAI_API_KEY"]
14
- config.gemini_api_key = ENV["GEMINI_API_KEY"]
23
+ # Access nested values with dot notation
24
+ RobotLab.config.ruby_llm.model #=> "claude-sonnet-4"
25
+ RobotLab.config.ruby_llm.anthropic_api_key #=> "sk-ant-..."
26
+ RobotLab.config.ruby_llm.request_timeout #=> 120
27
+ RobotLab.config.max_iterations #=> 10
28
+ RobotLab.config.streaming_enabled #=> true
29
+
30
+ # Check the environment
31
+ RobotLab.config.development? #=> true/false
32
+ ```
15
33
 
16
- # Default settings
17
- config.default_provider = :anthropic
18
- config.default_model = "claude-sonnet-4"
34
+ !!! warning "No configure block"
35
+ RobotLab does **not** use a `RobotLab.configure do |config| ... end` pattern. All configuration comes from config files, environment variables, or direct assignment on `RobotLab.config`.
19
36
 
20
- # Execution limits
21
- config.max_iterations = 10 # Max robots per network run
22
- config.max_tool_iterations = 10 # Max tool calls per robot run
37
+ ## Environment Variables
23
38
 
24
- # Streaming
25
- config.streaming_enabled = true
39
+ Environment variables use the `ROBOT_LAB_` prefix. Use double underscores (`__`) for nested values:
26
40
 
27
- # Logging
28
- config.logger = Logger.new($stdout)
41
+ ```bash
42
+ # Top-level settings
43
+ export ROBOT_LAB_MAX_ITERATIONS=20
44
+ export ROBOT_LAB_STREAMING_ENABLED=false
45
+
46
+ # Nested ruby_llm settings (note the double underscore)
47
+ export ROBOT_LAB_RUBY_LLM__MODEL=claude-sonnet-4
48
+ export ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY=sk-ant-...
49
+ export ROBOT_LAB_RUBY_LLM__OPENAI_API_KEY=sk-...
50
+ export ROBOT_LAB_RUBY_LLM__GEMINI_API_KEY=...
51
+ export ROBOT_LAB_RUBY_LLM__REQUEST_TIMEOUT=180
52
+ export ROBOT_LAB_RUBY_LLM__MAX_RETRIES=5
53
+ ```
54
+
55
+ The double underscore convention maps to nested YAML structure:
29
56
 
30
- # Template path for prompt files
31
- config.template_path = "prompts"
32
- end
57
+ ```
58
+ ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY --> ruby_llm.anthropic_api_key
59
+ ROBOT_LAB_RUBY_LLM__MODEL --> ruby_llm.model
60
+ ROBOT_LAB_MAX_ITERATIONS --> max_iterations
33
61
  ```
34
62
 
35
- ## Configuration Options
63
+ ## Config Files
36
64
 
37
- ### API Keys
65
+ ### Project Config
38
66
 
39
- | Option | Description |
40
- |--------|-------------|
41
- | `anthropic_api_key` | Anthropic Claude API key |
42
- | `openai_api_key` | OpenAI API key |
43
- | `gemini_api_key` | Google Gemini API key |
44
- | `bedrock_api_key` | AWS Bedrock API key |
45
- | `openrouter_api_key` | OpenRouter API key |
67
+ Create `./config/robot_lab.yml` in your project root:
46
68
 
47
- ### Defaults
69
+ ```yaml title="config/robot_lab.yml"
70
+ defaults:
71
+ ruby_llm:
72
+ anthropic_api_key: <%= ENV['ANTHROPIC_API_KEY'] %>
73
+ model: claude-sonnet-4
74
+ request_timeout: 120
48
75
 
49
- | Option | Default | Description |
50
- |--------|---------|-------------|
51
- | `default_provider` | `:anthropic` | Default LLM provider |
52
- | `default_model` | `"claude-sonnet-4"` | Default model |
53
- | `max_iterations` | `10` | Max robots per network run |
54
- | `max_tool_iterations` | `10` | Max tool calls per robot |
55
- | `streaming_enabled` | `true` | Enable streaming by default |
76
+ max_iterations: 15
77
+ template_path: prompts
78
+
79
+ development:
80
+ ruby_llm:
81
+ log_level: :debug
82
+
83
+ test:
84
+ max_iterations: 3
85
+ streaming_enabled: false
86
+ ruby_llm:
87
+ model: claude-haiku-3-5
88
+ request_timeout: 30
89
+ max_retries: 1
90
+
91
+ production:
92
+ max_iterations: 20
93
+ ruby_llm:
94
+ request_timeout: 180
95
+ max_retries: 5
96
+ log_level: :warn
97
+ ```
98
+
99
+ !!! tip "ERB support"
100
+ Config files support ERB templating, so you can reference environment variables with `<%= ENV['...'] %>`. This is useful for keeping secrets out of config files while still using YAML structure.
101
+
102
+ ### User Config
103
+
104
+ Create `~/.config/robot_lab/config.yml` for personal defaults that apply across all your projects:
56
105
 
57
- ### Templates
106
+ ```yaml title="~/.config/robot_lab/config.yml"
107
+ defaults:
108
+ ruby_llm:
109
+ anthropic_api_key: <%= ENV['ANTHROPIC_API_KEY'] %>
110
+ model: claude-sonnet-4
111
+ ```
112
+
113
+ ## Configuration Reference
58
114
 
59
- | Option | Default | Description |
60
- |--------|---------|-------------|
61
- | `template_path` | `"prompts"` (or `"app/prompts"` in Rails) | Directory for prompt templates |
115
+ ### Core Settings
62
116
 
63
- ### Global MCP & Tools
117
+ | Key | Default | Description |
118
+ |-----|---------|-------------|
119
+ | `max_iterations` | `10` | Maximum robots per network run |
120
+ | `max_tool_iterations` | `10` | Maximum tool calls per robot run |
121
+ | `streaming_enabled` | `true` | Enable streaming by default |
122
+ | `template_path` | `null` (auto-detected) | Directory for prompt templates |
123
+ | `mcp` | `:none` | Global MCP server configuration |
124
+ | `tools` | `:none` | Global tool whitelist |
125
+
126
+ ### RubyLLM Settings (`ruby_llm:` section)
127
+
128
+ All settings under the `ruby_llm:` key are applied to `RubyLLM.configure` automatically on startup.
129
+
130
+ #### Provider API Keys
131
+
132
+ | Key | Description |
133
+ |-----|-------------|
134
+ | `ruby_llm.anthropic_api_key` | Anthropic Claude API key |
135
+ | `ruby_llm.openai_api_key` | OpenAI API key |
136
+ | `ruby_llm.gemini_api_key` | Google Gemini API key |
137
+ | `ruby_llm.deepseek_api_key` | DeepSeek API key |
138
+ | `ruby_llm.mistral_api_key` | Mistral API key |
139
+ | `ruby_llm.openrouter_api_key` | OpenRouter API key |
140
+ | `ruby_llm.bedrock_api_key` | AWS Bedrock access key |
141
+ | `ruby_llm.bedrock_secret_key` | AWS Bedrock secret key |
142
+ | `ruby_llm.bedrock_region` | AWS Bedrock region |
143
+ | `ruby_llm.xai_api_key` | xAI (Grok) API key |
144
+
145
+ #### Model Defaults
146
+
147
+ | Key | Default | Description |
148
+ |-----|---------|-------------|
149
+ | `ruby_llm.provider` | `:anthropic` | Default LLM provider |
150
+ | `ruby_llm.model` | `claude-sonnet-4` | Default model for robots |
151
+ | `ruby_llm.default_model` | `null` | RubyLLM default model override |
152
+ | `ruby_llm.default_embedding_model` | `null` | Default embedding model |
153
+ | `ruby_llm.default_image_model` | `null` | Default image model |
154
+
155
+ #### Connection Settings
156
+
157
+ | Key | Default | Description |
158
+ |-----|---------|-------------|
159
+ | `ruby_llm.request_timeout` | `120` | Request timeout in seconds |
160
+ | `ruby_llm.max_retries` | `3` | Maximum retry attempts |
161
+ | `ruby_llm.retry_interval` | `1` | Seconds between retries |
162
+ | `ruby_llm.retry_backoff_factor` | `2` | Exponential backoff factor |
163
+ | `ruby_llm.http_proxy` | `null` | HTTP proxy URL |
164
+
165
+ #### Provider Endpoints (self-hosted models)
166
+
167
+ | Key | Description |
168
+ |-----|-------------|
169
+ | `ruby_llm.openai_api_base` | Custom OpenAI-compatible endpoint |
170
+ | `ruby_llm.gemini_api_base` | Custom Gemini endpoint |
171
+ | `ruby_llm.ollama_api_base` | Ollama endpoint (e.g., `http://localhost:11434`) |
172
+ | `ruby_llm.gpustack_api_base` | GPUStack endpoint |
173
+
174
+ #### Logging
175
+
176
+ | Key | Default | Description |
177
+ |-----|---------|-------------|
178
+ | `ruby_llm.log_file` | `null` | Path to log file |
179
+ | `ruby_llm.log_level` | `:info` | Log level (`:debug`, `:info`, `:warn`, `:error`) |
180
+ | `ruby_llm.log_stream_debug` | `false` | Log streaming debug output |
181
+
182
+ ### Chat Configuration (`chat:` section)
183
+
184
+ Default chat parameters applied to all robots unless overridden:
185
+
186
+ | Key | Default | Description |
187
+ |-----|---------|-------------|
188
+ | `chat.with_temperature` | `0.7` | Controls randomness (0.0-2.0) |
189
+ | `chat.with_params.top_p` | `null` | Nucleus sampling threshold |
190
+ | `chat.with_params.top_k` | `null` | Top-k sampling |
191
+ | `chat.with_params.max_tokens` | `null` | Maximum tokens in response |
192
+ | `chat.with_params.presence_penalty` | `null` | Presence penalty (-2.0 to 2.0) |
193
+ | `chat.with_params.frequency_penalty` | `null` | Frequency penalty (-2.0 to 2.0) |
194
+ | `chat.with_params.stop` | `null` | Stop sequences |
195
+
196
+ ## Runtime-Only Attributes
197
+
198
+ Some attributes can only be set at runtime, not through config files:
64
199
 
65
200
  ```ruby
66
- RobotLab.configure do |config|
67
- # Global MCP servers available to all networks
68
- config.mcp = [
69
- { name: "github", transport: { type: "stdio", command: "github-mcp" } }
70
- ]
71
-
72
- # Global tool whitelist
73
- config.tools = %w[search_code create_issue]
74
- end
201
+ # Logger (defaults to Rails.logger in Rails, or Logger.new($stdout) otherwise)
202
+ RobotLab.config.logger = Logger.new(nil) # silence logging
203
+ RobotLab.config.logger = Logger.new("robot.log") # log to file
75
204
  ```
76
205
 
77
- ## Network-Level Configuration
206
+ ## Reloading Configuration
78
207
 
79
- Override global settings at the network level:
208
+ To reload configuration from all sources:
80
209
 
81
210
  ```ruby
82
- network = RobotLab.create_network do
83
- name "my_network"
211
+ RobotLab.reload_config!
212
+ ```
84
213
 
85
- # Override default model for this network
86
- default_model "claude-sonnet-4"
214
+ This clears the cached config and reloads from all sources on next access.
87
215
 
88
- # Network-specific MCP servers
89
- mcp [
90
- { name: "filesystem", transport: { type: "stdio", command: "mcp-fs" } }
91
- ]
216
+ ## Environment-Specific Configuration
92
217
 
93
- # Network-specific tool whitelist
94
- tools %w[read_file write_file]
218
+ The `defaults.yml` shipped with RobotLab includes environment-specific overrides:
95
219
 
96
- # Or inherit from global
97
- mcp :inherit
98
- tools :inherit
99
- end
100
- ```
220
+ === "Development"
101
221
 
102
- ## Robot-Level Configuration
222
+ ```yaml
223
+ development:
224
+ ruby_llm:
225
+ log_level: :debug
226
+ ```
103
227
 
104
- Configure individual robots:
228
+ === "Test"
105
229
 
106
- ```ruby
107
- robot = RobotLab.build do
108
- name "specialist"
230
+ ```yaml
231
+ test:
232
+ max_iterations: 3
233
+ streaming_enabled: false
234
+ ruby_llm:
235
+ model: claude-haiku-3-5
236
+ request_timeout: 30
237
+ max_retries: 1
238
+ log_level: :warn
239
+ ```
109
240
 
110
- # Robot-specific model
111
- model "claude-sonnet-4"
241
+ === "Production"
112
242
 
113
- # Robot-specific MCP (overrides network)
114
- mcp :inherit # Use network's MCP servers
115
- # or
116
- mcp :none # No MCP servers for this robot
117
- # or
118
- mcp [...] # Specific servers
243
+ ```yaml
244
+ production:
245
+ streaming_enabled: false
246
+ max_iterations: 20
247
+ ruby_llm:
248
+ request_timeout: 180
249
+ max_retries: 5
250
+ log_level: :warn
251
+ ```
119
252
 
120
- # Robot-specific tools
121
- tools :inherit # Use network's tools
122
- end
123
- ```
253
+ The current environment is determined automatically (via `RAILS_ENV`, `RACK_ENV`, or defaults to `development`).
254
+
255
+ ## Rails Integration
256
+
257
+ In Rails, RobotLab is configured automatically via its Railtie. The logger defaults to `Rails.logger`, and templates default to `app/prompts/`.
258
+
259
+ Create a project config file for Rails-specific settings:
124
260
 
125
- ## Configuration Hierarchy
261
+ ```yaml title="config/robot_lab.yml"
262
+ defaults:
263
+ ruby_llm:
264
+ anthropic_api_key: <%= Rails.application.credentials.anthropic_api_key %>
265
+ model: claude-sonnet-4
126
266
 
127
- Configuration cascades from global to network to robot:
267
+ template_path: null # auto-detects app/prompts in Rails
128
268
 
269
+ production:
270
+ ruby_llm:
271
+ request_timeout: 180
272
+ max_retries: 5
129
273
  ```
130
- Global (RobotLab.configure)
131
- └── Network (create_network)
132
- └── Robot (build)
133
- └── Runtime (robot.run)
274
+
275
+ You can also use Rails credentials:
276
+
277
+ ```bash
278
+ rails credentials:edit
134
279
  ```
135
280
 
136
- Each level can:
281
+ ```yaml
282
+ # config/credentials.yml.enc
283
+ anthropic_api_key: sk-ant-...
284
+ openai_api_key: sk-...
285
+ ```
137
286
 
138
- - `:inherit` - Use parent level's configuration
139
- - `:none` or `nil` or `[]` - No items allowed
140
- - `[items]` - Specific items only
287
+ Then reference them in your config file with ERB:
141
288
 
142
- ## Rails Configuration
289
+ ```yaml title="config/robot_lab.yml"
290
+ defaults:
291
+ ruby_llm:
292
+ anthropic_api_key: <%= Rails.application.credentials.anthropic_api_key %>
293
+ ```
294
+
295
+ ## RunConfig: Shared Operational Defaults
143
296
 
144
- In Rails, configure in an initializer:
297
+ `RunConfig` is a configuration object that lets you express operational defaults for LLM settings, tools, callbacks, and infrastructure. Unlike `RobotLab.config` (which is global and static), RunConfig flows through the hierarchy and can be customized at each level:
145
298
 
146
- ```ruby title="config/initializers/robot_lab.rb"
147
- RobotLab.configure do |config|
148
- # Use Rails credentials
149
- config.anthropic_api_key = Rails.application.credentials.anthropic_api_key
299
+ ```
300
+ RobotLab.config (global) -> Network RunConfig -> Robot RunConfig -> Template front matter -> Task RunConfig -> Runtime
301
+ ```
150
302
 
151
- # Use Rails logger
152
- config.logger = Rails.logger
303
+ ### Creating a RunConfig
153
304
 
154
- # Template path is automatically set to app/prompts
305
+ ```ruby
306
+ # Keyword construction
307
+ config = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.7)
308
+
309
+ # Block DSL
310
+ config = RobotLab::RunConfig.new do |c|
311
+ c.model "claude-sonnet-4"
312
+ c.temperature 0.7
313
+ c.max_tokens 2000
155
314
  end
315
+
316
+ # Chaining
317
+ config = RobotLab::RunConfig.new
318
+ .model("claude-sonnet-4")
319
+ .temperature(0.7)
156
320
  ```
157
321
 
158
- Or use `config/application.rb`:
322
+ ### Applying RunConfig
323
+
324
+ Pass `config:` to robots and networks. Explicit constructor kwargs always override the RunConfig:
159
325
 
160
- ```ruby title="config/application.rb"
161
- module MyApp
162
- class Application < Rails::Application
163
- config.robot_lab.default_model = "claude-sonnet-4"
164
- config.robot_lab.default_provider = :anthropic
165
- end
326
+ ```ruby
327
+ # Shared config for a team of robots
328
+ shared = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.5)
329
+
330
+ # Robot uses shared config
331
+ robot = RobotLab.build(
332
+ name: "writer",
333
+ system_prompt: "You are a creative writer.",
334
+ config: shared,
335
+ temperature: 0.9 # overrides shared config's 0.5
336
+ )
337
+
338
+ # Network applies config to all member robots
339
+ network = RobotLab.create_network(name: "pipeline", config: shared) do
340
+ task :analyzer, analyzer_robot, depends_on: :none
341
+ task :writer, writer_robot, depends_on: [:analyzer]
166
342
  end
167
343
  ```
168
344
 
169
- ## Environment-Specific Configuration
345
+ ### Merging Configs
170
346
 
171
- ```ruby title="config/initializers/robot_lab.rb"
172
- RobotLab.configure do |config|
173
- config.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
174
-
175
- case Rails.env
176
- when "development"
177
- config.logger = Logger.new($stdout, level: :debug)
178
- config.default_model = "claude-haiku-3" # Faster/cheaper for dev
179
- when "test"
180
- config.streaming_enabled = false
181
- when "production"
182
- config.logger = Rails.logger
183
- config.default_model = "claude-sonnet-4"
184
- end
185
- end
347
+ RunConfig supports merge semantics where the more-specific config's values win:
348
+
349
+ ```ruby
350
+ network_config = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.5)
351
+ robot_config = RobotLab::RunConfig.new(temperature: 0.9)
352
+ effective = network_config.merge(robot_config)
353
+ effective.model #=> "claude-sonnet-4" (inherited)
354
+ effective.temperature #=> 0.9 (overridden)
186
355
  ```
187
356
 
188
- ## Using Environment Variables
357
+ ### Available Fields
189
358
 
190
- Recommended environment variables:
359
+ | Category | Fields |
360
+ |----------|--------|
361
+ | **LLM** | `model`, `temperature`, `top_p`, `top_k`, `max_tokens`, `presence_penalty`, `frequency_penalty`, `stop` |
362
+ | **Tools** | `mcp`, `tools` |
363
+ | **Callbacks** | `on_tool_call`, `on_tool_result` |
364
+ | **Infrastructure** | `bus`, `enable_cache` |
191
365
 
192
- ```bash
193
- # Required - at least one provider
194
- ANTHROPIC_API_KEY=sk-ant-...
195
- OPENAI_API_KEY=sk-...
196
- GEMINI_API_KEY=...
366
+ ### RunConfig vs RobotLab.config
197
367
 
198
- # Optional - override defaults
199
- ROBOT_LAB_DEFAULT_MODEL=claude-sonnet-4
200
- ROBOT_LAB_DEFAULT_PROVIDER=anthropic
201
- ROBOT_LAB_MAX_ITERATIONS=20
202
- ```
368
+ | | `RobotLab.config` | `RunConfig` |
369
+ |---|---|---|
370
+ | **Scope** | Global (all robots) | Per-network, per-robot, or per-task |
371
+ | **Source** | YAML files, env vars | Code (constructor, block DSL) |
372
+ | **Mutability** | Loaded once, rarely changed | Created per use case, merged |
373
+ | **Purpose** | API keys, timeouts, defaults | Model, temperature, tools per workflow |
203
374
 
204
- Load them in configuration:
375
+ ## Robot-Level Configuration
376
+
377
+ Individual robots can override the global model and other settings:
205
378
 
206
379
  ```ruby
207
- RobotLab.configure do |config|
208
- config.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
209
- config.default_model = ENV.fetch("ROBOT_LAB_DEFAULT_MODEL", "claude-sonnet-4")
210
- config.max_iterations = ENV.fetch("ROBOT_LAB_MAX_ITERATIONS", 10).to_i
211
- end
380
+ # Override model for a specific robot
381
+ robot = RobotLab.build(
382
+ name: "fast_bot",
383
+ system_prompt: "You are a quick responder.",
384
+ model: "claude-haiku-3-5",
385
+ temperature: 0.3,
386
+ max_tokens: 500
387
+ )
388
+
389
+ # Or use chaining at runtime
390
+ robot.with_temperature(0.9).with_max_tokens(2000).run("Tell me a story.")
212
391
  ```
213
392
 
214
- ## Accessing Configuration
393
+ ## Hierarchical MCP and Tools
215
394
 
216
- ```ruby
217
- # Get current configuration
218
- config = RobotLab.configuration
395
+ MCP servers and tools use a hierarchical configuration: `runtime > robot > network > global`. Each level can specify:
396
+
397
+ - `:inherit` -- Use the parent level's configuration
398
+ - `:none` -- No MCP servers or tools at this level
399
+ - An explicit array -- Specific servers or tools
219
400
 
220
- # Check settings
221
- config.default_model # => "claude-sonnet-4"
222
- config.default_provider # => :anthropic
223
- config.streaming_enabled # => true
401
+ ```ruby
402
+ # Robot inheriting network MCP config
403
+ robot = RobotLab.build(
404
+ name: "agent",
405
+ system_prompt: "You are helpful.",
406
+ mcp: :inherit,
407
+ tools: :inherit
408
+ )
409
+
410
+ # Robot with no MCP, specific tools
411
+ robot = RobotLab.build(
412
+ name: "calculator",
413
+ system_prompt: "You solve math problems.",
414
+ mcp: :none,
415
+ local_tools: [Calculator]
416
+ )
224
417
  ```
225
418
 
226
419
  ## Next Steps
@@ -15,7 +15,7 @@ In this section, you'll learn how to:
15
15
 
16
16
  Before you begin, make sure you have:
17
17
 
18
- - **Ruby 3.1+** installed
18
+ - **Ruby 3.2+** installed
19
19
  - An **API key** from at least one LLM provider:
20
20
  - [Anthropic](https://console.anthropic.com/) (recommended)
21
21
  - [OpenAI](https://platform.openai.com/)
@@ -4,7 +4,7 @@ This guide covers installing RobotLab in your Ruby project.
4
4
 
5
5
  ## Requirements
6
6
 
7
- - **Ruby**: 3.1 or higher
7
+ - **Ruby**: 3.2 or higher
8
8
  - **Bundler**: 2.0 or higher (recommended)
9
9
 
10
10
  ## Install via Bundler
@@ -31,13 +31,19 @@ gem install robot_lab
31
31
 
32
32
  ## Dependencies
33
33
 
34
- RobotLab automatically installs these dependencies:
34
+ RobotLab automatically installs these core dependencies:
35
35
 
36
36
  | Gem | Purpose |
37
37
  |-----|---------|
38
- | `ruby_llm` | LLM provider integrations |
39
- | `ruby_llm-template` | Template rendering for prompts |
40
- | `simple_flow` | Workflow execution |
38
+ | `ruby_llm` (~> 1.12) | LLM provider integrations (Anthropic, OpenAI, Gemini, etc.) |
39
+ | `prompt_manager` (~> 1.0) | Template-based prompt management with YAML front matter |
40
+ | `simple_flow` (~> 0.3) | Pipeline workflow execution for networks |
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 |
45
+ | `zeitwerk` (~> 2.6) | Autoloading and eager loading |
46
+ | `async` (~> 2.0) | Fiber-based concurrency |
41
47
 
42
48
  ### Optional Dependencies
43
49
 
@@ -78,7 +84,7 @@ Run it:
78
84
 
79
85
  ```bash
80
86
  ruby test_robot_lab.rb
81
- # => RobotLab version: 0.0.1
87
+ # => RobotLab version: 0.1.0
82
88
  # => Installation successful!
83
89
  ```
84
90
 
@@ -107,24 +113,24 @@ rails db:migrate
107
113
 
108
114
  ## Environment Setup
109
115
 
110
- Before using RobotLab, set up your API keys as environment variables:
116
+ RobotLab uses a layered configuration system (see [Configuration](configuration.md) for full details). The simplest way to get started is with environment variables:
111
117
 
112
118
  === "Anthropic (Recommended)"
113
119
 
114
120
  ```bash
115
- export ANTHROPIC_API_KEY="sk-ant-..."
121
+ export ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY="sk-ant-..."
116
122
  ```
117
123
 
118
124
  === "OpenAI"
119
125
 
120
126
  ```bash
121
- export OPENAI_API_KEY="sk-..."
127
+ export ROBOT_LAB_RUBY_LLM__OPENAI_API_KEY="sk-..."
122
128
  ```
123
129
 
124
130
  === "Google Gemini"
125
131
 
126
132
  ```bash
127
- export GEMINI_API_KEY="..."
133
+ export ROBOT_LAB_RUBY_LLM__GEMINI_API_KEY="..."
128
134
  ```
129
135
 
130
136
  !!! tip "Using dotenv"
@@ -132,14 +138,17 @@ Before using RobotLab, set up your API keys as environment variables:
132
138
 
133
139
  ```ruby
134
140
  # Gemfile
135
- gem "dotenv-rails", groups: [:development, :test]
141
+ gem "dotenv", groups: [:development, :test]
136
142
  ```
137
143
 
138
144
  ```bash
139
145
  # .env
140
- ANTHROPIC_API_KEY=sk-ant-...
146
+ ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY=sk-ant-...
141
147
  ```
142
148
 
149
+ !!! info "Direct provider env vars"
150
+ RubyLLM also reads provider-specific environment variables directly (e.g., `ANTHROPIC_API_KEY`). If you already have those set, they will be picked up automatically. The `ROBOT_LAB_RUBY_LLM__*` prefix gives you explicit control through RobotLab's config layer.
151
+
143
152
  ## Troubleshooting
144
153
 
145
154
  ### Gem Installation Fails
@@ -167,7 +176,7 @@ bundle add async-websocket
167
176
 
168
177
  If you see authentication errors:
169
178
 
170
- 1. Verify your API key is set: `echo $ANTHROPIC_API_KEY`
179
+ 1. Verify your API key is set: `echo $ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY`
171
180
  2. Check the key is valid in your provider's console
172
181
  3. Ensure you're using the correct environment variable name
173
182