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
@@ -2,27 +2,67 @@
2
2
 
3
3
  Callable function that robots can use to interact with external systems.
4
4
 
5
- ## Class: `RobotLab::Tool`
5
+ ## Class: `RobotLab::Tool < RubyLLM::Tool`
6
+
7
+ RobotLab::Tool inherits from RubyLLM::Tool, adding a `robot:` constructor parameter and a `Tool.create` factory for dynamic tools.
8
+
9
+ ### Subclass Pattern
10
+
11
+ ```ruby
12
+ class GetWeather < RobotLab::Tool
13
+ description "Get weather for a location"
14
+
15
+ param :location, type: "string", desc: "City name or zip code"
16
+ param :unit, type: "string", desc: "Temperature unit", required: false
17
+
18
+ def execute(location:, unit: "celsius")
19
+ WeatherService.current(location, unit: unit)
20
+ end
21
+ end
22
+ ```
23
+
24
+ ### Factory Pattern
6
25
 
7
26
  ```ruby
8
- tool = RobotLab::Tool.new(
27
+ tool = RobotLab::Tool.create(
9
28
  name: "get_weather",
10
- description: "Get weather for a location",
11
- parameters: { location: { type: "string", required: true } },
12
- handler: ->(location:, **_) { WeatherService.fetch(location) }
13
- )
29
+ description: "Get current weather for a location",
30
+ parameters: {
31
+ type: "object",
32
+ properties: {
33
+ location: { type: "string", description: "City name" }
34
+ },
35
+ required: ["location"]
36
+ }
37
+ ) { |args| WeatherService.current(args[:location]) }
14
38
  ```
15
39
 
16
40
  ## Constructor
17
41
 
18
42
  ```ruby
19
- Tool.new(
43
+ Tool.new(robot: nil)
44
+ ```
45
+
46
+ **Parameters:**
47
+
48
+ | Name | Type | Description |
49
+ |------|------|-------------|
50
+ | `robot` | `Robot, nil` | The owning robot instance |
51
+
52
+ ## Class Methods
53
+
54
+ ### Tool.create
55
+
56
+ Factory for dynamic tools (MCP wrappers, inline tools).
57
+
58
+ ```ruby
59
+ Tool.create(
20
60
  name:,
21
61
  description: nil,
22
- parameters: {},
23
- handler:,
24
- mcp: false,
25
- strict: false
62
+ parameters: nil,
63
+ mcp: nil,
64
+ robot: nil,
65
+ &handler
26
66
  )
27
67
  ```
28
68
 
@@ -30,321 +70,277 @@ Tool.new(
30
70
 
31
71
  | Name | Type | Description |
32
72
  |------|------|-------------|
33
- | `name` | `String` | Tool identifier |
73
+ | `name` | `String, Symbol` | Tool identifier |
34
74
  | `description` | `String` | What the tool does |
35
- | `parameters` | `Hash` | Parameter definitions |
36
- | `handler` | `Proc` | Execution function |
37
- | `mcp` | `Boolean` | Is this an MCP tool? |
38
- | `strict` | `Boolean` | Strict parameter validation |
75
+ | `parameters` | `Hash` | JSON Schema parameter definition |
76
+ | `mcp` | `String` | MCP server name |
77
+ | `robot` | `Robot` | Owning robot instance |
78
+ | `&handler` | `Block` | Receives `args` hash, returns result |
39
79
 
40
- ## Attributes
80
+ ## Inherited DSL (from RubyLLM::Tool)
41
81
 
42
- ### name
82
+ ### description
43
83
 
44
84
  ```ruby
45
- tool.name # => String
85
+ class MyTool < RobotLab::Tool
86
+ description "What this tool does"
87
+ end
46
88
  ```
47
89
 
48
- Tool identifier.
90
+ ### param
49
91
 
50
- ### description
92
+ ```ruby
93
+ class MyTool < RobotLab::Tool
94
+ param :name, type: "string", desc: "User's name"
95
+ param :age, type: "integer", desc: "User's age", required: false
96
+ end
97
+ ```
98
+
99
+ ### execute
51
100
 
52
101
  ```ruby
53
- tool.description # => String
102
+ class MyTool < RobotLab::Tool
103
+ def execute(name:, age: nil)
104
+ # Implementation
105
+ end
106
+ end
54
107
  ```
55
108
 
56
- Human-readable description.
109
+ ### halt
57
110
 
58
- ### parameters
111
+ Stop the tool use loop from within execute:
59
112
 
60
113
  ```ruby
61
- tool.parameters # => Hash
114
+ def execute(**)
115
+ halt("Done processing")
116
+ end
62
117
  ```
63
118
 
64
- Parameter schema.
119
+ ### with_params
65
120
 
66
- ### handler
121
+ Set provider-specific parameters:
67
122
 
68
123
  ```ruby
69
- tool.handler # => Proc
124
+ class MyTool < RobotLab::Tool
125
+ with_params(strict: true)
126
+ end
70
127
  ```
71
128
 
72
- Function that executes the tool.
129
+ ## Attributes
73
130
 
74
- ### mcp
131
+ ### robot
75
132
 
76
133
  ```ruby
77
- tool.mcp # => Boolean
134
+ tool.robot # => Robot or nil
135
+ tool.robot = some_robot
78
136
  ```
79
137
 
80
- Whether this is an MCP-sourced tool.
138
+ Read/write accessor for the owning robot. Set via constructor or assigned later.
81
139
 
82
- ### strict
140
+ ### mcp
83
141
 
84
142
  ```ruby
85
- tool.strict # => Boolean
143
+ tool.mcp # => String or nil
86
144
  ```
87
145
 
88
- Whether to use strict parameter validation.
146
+ The MCP server name, set via `Tool.create(mcp: "server_name")`.
89
147
 
90
148
  ## Methods
91
149
 
92
- ### call
150
+ ### name
93
151
 
94
152
  ```ruby
95
- result = tool.call(
96
- params,
97
- robot: robot,
98
- network: network,
99
- state: state
100
- )
153
+ tool.name # => String
101
154
  ```
102
155
 
103
- Execute the tool with parameters.
156
+ Returns the tool name. For subclasses, derived from the class name (CamelCase to snake_case). For `create`d tools, returns the explicit name.
104
157
 
105
- ### to_h
158
+ ### mcp?
106
159
 
107
160
  ```ruby
108
- tool.to_h # => Hash
161
+ tool.mcp? # => Boolean
109
162
  ```
110
163
 
111
- Hash representation.
164
+ Whether this is an MCP-provided tool.
112
165
 
113
- ### to_json
166
+ ### call
114
167
 
115
168
  ```ruby
116
- tool.to_json # => String
169
+ result = tool.call(args_hash)
117
170
  ```
118
171
 
119
- JSON representation.
172
+ Inherited from RubyLLM::Tool. Converts string keys to symbols and calls `execute(**args)`.
120
173
 
121
- ### to_json_schema
174
+ ### params_schema
122
175
 
123
176
  ```ruby
124
- tool.to_json_schema # => Hash
177
+ tool.params_schema # => Hash or nil
125
178
  ```
126
179
 
127
- JSON Schema representation for LLM.
128
-
129
- ## Parameter Schema
180
+ Inherited. Returns the JSON Schema for tool parameters.
130
181
 
131
- ### Basic Types
182
+ ### provider_params
132
183
 
133
184
  ```ruby
134
- parameters: {
135
- name: { type: "string" },
136
- count: { type: "integer" },
137
- price: { type: "number" },
138
- active: { type: "boolean" },
139
- tags: { type: "array" }
140
- }
185
+ tool.provider_params # => Hash
141
186
  ```
142
187
 
143
- ### Required Parameters
188
+ Inherited. Returns provider-specific parameters (e.g., `{ strict: true }`).
189
+
190
+ ### to_h
144
191
 
145
192
  ```ruby
146
- parameters: {
147
- id: { type: "string", required: true }
148
- }
193
+ tool.to_h # => Hash
149
194
  ```
150
195
 
151
- ### Descriptions
196
+ Hash representation with `:name`, `:description`, `:mcp`.
197
+
198
+ ### to_json
152
199
 
153
200
  ```ruby
154
- parameters: {
155
- query: {
156
- type: "string",
157
- description: "Search query (supports wildcards)"
158
- }
159
- }
201
+ tool.to_json # => String
160
202
  ```
161
203
 
162
- ### Enums
204
+ JSON representation.
205
+
206
+ ### to_json_schema
163
207
 
164
208
  ```ruby
165
- parameters: {
166
- status: {
167
- type: "string",
168
- enum: ["pending", "active", "completed"]
169
- }
170
- }
209
+ tool.to_json_schema # => Hash
171
210
  ```
172
211
 
173
- ### Defaults
212
+ JSON Schema representation for LLM function calling. Returns `{ name:, description:, parameters: }`.
213
+
214
+ ## Robot-Aware Tools
215
+
216
+ Tools that modify their owning robot use the `robot` accessor:
174
217
 
175
218
  ```ruby
176
- parameters: {
177
- limit: {
178
- type: "integer",
179
- default: 10
180
- }
181
- }
219
+ class AdjustTemperature < RobotLab::Tool
220
+ description "Adjust the robot's creativity level"
221
+
222
+ param :level, type: "number", desc: "Temperature from 0.0 to 1.0"
223
+
224
+ def execute(level:)
225
+ robot.with_temperature(level)
226
+ "Temperature adjusted to #{level}"
227
+ end
228
+ end
229
+
230
+ # Pass robot: self when constructing
231
+ robot = RobotLab.build(
232
+ name: "creative_bot",
233
+ system_prompt: "You are creative.",
234
+ local_tools: [AdjustTemperature.new(robot: self)]
235
+ )
182
236
  ```
183
237
 
184
- ## Handler
238
+ ## Parameter Types
185
239
 
186
- ### Basic Handler
240
+ ### String
187
241
 
188
242
  ```ruby
189
- handler: ->(param:, **_context) {
190
- do_something(param)
191
- }
243
+ param :name, type: "string", desc: "User's full name"
192
244
  ```
193
245
 
194
- ### With Context
246
+ ### Integer
195
247
 
196
248
  ```ruby
197
- handler: ->(param:, robot:, network:, state:) {
198
- # robot - The executing Robot
199
- # network - The NetworkRun
200
- # state - Current State
249
+ param :count, type: "integer", desc: "Number of results"
250
+ ```
201
251
 
202
- user = state.data[:user_id]
203
- state.memory.remember("last_action", param)
252
+ ### Number (Float)
204
253
 
205
- perform_action(param, user)
206
- }
254
+ ```ruby
255
+ param :price, type: "number", desc: "Price in dollars"
207
256
  ```
208
257
 
209
- ### Error Handling
258
+ ### Boolean
210
259
 
211
260
  ```ruby
212
- handler: ->(id:, **_) {
213
- record = Record.find_by(id: id)
214
-
215
- if record
216
- { success: true, data: record.to_h }
217
- else
218
- { success: false, error: "Not found" }
219
- end
220
- rescue StandardError => e
221
- { success: false, error: e.message }
222
- }
261
+ param :active, type: "boolean", desc: "Whether the user is active"
223
262
  ```
224
263
 
225
- ## Builder DSL
264
+ ### Required vs Optional
226
265
 
227
- In robot builder:
266
+ Parameters are required by default. Mark optional with `required: false`:
228
267
 
229
268
  ```ruby
230
- tool :tool_name do
231
- description "What it does"
269
+ param :query, type: "string", desc: "Search query" # required
270
+ param :limit, type: "integer", desc: "Max results", required: false # optional
271
+ ```
232
272
 
233
- parameter :param1, type: :string, required: true
234
- parameter :param2, type: :integer, default: 10
273
+ ## Built-in: AskUser
235
274
 
236
- handler do |param1:, param2:, **_context|
237
- # Implementation
238
- end
275
+ `RobotLab::AskUser` is a built-in tool that lets a robot ask the user a question via the terminal. The LLM decides when human input is needed and calls this tool.
276
+
277
+ ### Class: `RobotLab::AskUser < RobotLab::Tool`
278
+
279
+ ```ruby
280
+ class RobotLab::AskUser < RobotLab::Tool
281
+ description "Ask the user a question and wait for their typed response"
282
+ param :question, type: "string", desc: "The question to ask the user"
283
+ param :choices, type: "array", desc: "Optional list of choices to present", required: false
284
+ param :default, type: "string", desc: "Default value if user presses Enter", required: false
239
285
  end
240
286
  ```
241
287
 
242
- ## ToolManifest
288
+ ### Parameters
243
289
 
244
- Wrap tools with modified metadata:
290
+ | Name | Type | Required | Description |
291
+ |------|------|----------|-------------|
292
+ | `question` | `String` | Yes | The question to display |
293
+ | `choices` | `Array` | No | Numbered choices to present |
294
+ | `default` | `String` | No | Value returned when user presses Enter without typing |
245
295
 
246
- ```ruby
247
- manifest = RobotLab::ToolManifest.new(
248
- tool: original_tool,
249
- name: "custom_name",
250
- description: "Custom description"
251
- )
296
+ ### IO Resolution
252
297
 
253
- # Original tool is used, metadata is overridden
254
- ```
298
+ The tool reads input and writes output using the owning robot's `input`/`output` accessors:
255
299
 
256
- ### Attributes
300
+ 1. `robot.input` / `robot.output` if set
301
+ 2. Falls back to `$stdin` / `$stdout`
257
302
 
258
- - `tool` - The wrapped tool
259
- - `name` - Override name (or original)
260
- - `description` - Override description (or original)
261
- - `aliases` - Alternative names
303
+ ### Terminal Output
262
304
 
263
- ## Examples
305
+ ```
306
+ [robot_name] What programming language do you want to learn?
307
+ 1. Ruby
308
+ 2. Python
309
+ 3. Go
310
+ > [Ruby]
311
+ ```
264
312
 
265
- ### Simple Tool
313
+ ### Usage
266
314
 
267
315
  ```ruby
268
- tool = Tool.new(
269
- name: "current_time",
270
- description: "Get the current time",
271
- handler: ->(**, _) { Time.now.iso8601 }
316
+ robot = RobotLab.build(
317
+ name: "interviewer",
318
+ system_prompt: "Interview the user about their project needs. Use ask_user to gather information.",
319
+ local_tools: [RobotLab::AskUser]
272
320
  )
321
+ robot.run("Find out what the user wants to build")
273
322
  ```
274
323
 
275
- ### Tool with Parameters
324
+ ### Testing with StringIO
276
325
 
277
326
  ```ruby
278
- tool = Tool.new(
279
- name: "search_users",
280
- description: "Search users by criteria",
281
- parameters: {
282
- query: {
283
- type: "string",
284
- description: "Search query",
285
- required: true
286
- },
287
- limit: {
288
- type: "integer",
289
- description: "Max results",
290
- default: 10
291
- },
292
- status: {
293
- type: "string",
294
- enum: ["active", "inactive", "all"],
295
- default: "active"
296
- }
297
- },
298
- handler: ->(query:, limit:, status:, **_) {
299
- User.search(query, limit: limit, status: status)
300
- }
301
- )
327
+ robot = RobotLab::Robot.new(name: "bot", template: :assistant)
328
+ robot.input = StringIO.new("Ruby\n")
329
+ robot.output = StringIO.new
330
+
331
+ tool = RobotLab::AskUser.new(robot: robot)
332
+ result = tool.call("question" => "Pick a language:", "choices" => ["Ruby", "Python"])
333
+ # => "Ruby"
302
334
  ```
303
335
 
304
- ### API Integration Tool
336
+ ### Choice Mapping
305
337
 
306
- ```ruby
307
- tool = Tool.new(
308
- name: "fetch_stock_price",
309
- description: "Get current stock price",
310
- parameters: {
311
- symbol: { type: "string", required: true }
312
- },
313
- handler: ->(symbol:, **_) {
314
- response = HTTP.get("https://api.stocks.example/#{symbol}")
315
-
316
- if response.status.success?
317
- JSON.parse(response.body)
318
- else
319
- { error: "Failed to fetch", status: response.status.code }
320
- end
321
- rescue HTTP::Error => e
322
- { error: "Network error: #{e.message}" }
323
- }
324
- )
325
- ```
338
+ When `choices` are provided, the user can type either:
326
339
 
327
- ### Database Tool
340
+ - A **number** (e.g., `2`) — mapped to the corresponding choice text
341
+ - **Text** (e.g., `Python`) — returned as-is
328
342
 
329
- ```ruby
330
- tool = Tool.new(
331
- name: "get_order",
332
- description: "Get order details",
333
- parameters: {
334
- order_id: { type: "string", required: true }
335
- },
336
- handler: ->(order_id:, state:, **_) {
337
- user_id = state.data[:user_id]
338
- order = Order.find_by(id: order_id, user_id: user_id)
339
-
340
- if order
341
- order.as_json(include: [:items, :shipping])
342
- else
343
- { error: "Order not found or unauthorized" }
344
- end
345
- }
346
- )
347
- ```
343
+ Out-of-range numbers are returned as-is (the LLM can re-ask if needed).
348
344
 
349
345
  ## See Also
350
346
 
data/docs/api/index.md CHANGED
@@ -10,9 +10,8 @@ The fundamental building blocks of RobotLab:
10
10
  |-------|-------------|
11
11
  | [Robot](core/robot.md) | LLM-powered agent with personality and tools |
12
12
  | [Network](core/network.md) | Orchestrates multiple robots |
13
- | [State](core/state.md) | Manages conversation and workflow data |
13
+ | [Memory](core/memory.md) | Reactive key-value store for sharing data |
14
14
  | [Tool](core/tool.md) | Custom function robots can call |
15
- | [Memory](core/memory.md) | Shared key-value store |
16
15
 
17
16
  ## Messages
18
17
 
@@ -21,20 +20,10 @@ Message types for LLM communication:
21
20
  | Class | Description |
22
21
  |-------|-------------|
23
22
  | [UserMessage](messages/user-message.md) | User input with metadata |
24
- | [TextMessage](messages/text-message.md) | Assistant text response |
23
+ | [TextMessage](messages/text-message.md) | Text message with role |
25
24
  | [ToolCallMessage](messages/tool-call-message.md) | Tool execution request |
26
25
  | [ToolResultMessage](messages/tool-result-message.md) | Tool execution result |
27
26
 
28
- ## Adapters
29
-
30
- Provider-specific message conversion:
31
-
32
- | Class | Description |
33
- |-------|-------------|
34
- | [Anthropic](adapters/anthropic.md) | Claude models adapter |
35
- | [OpenAI](adapters/openai.md) | GPT models adapter |
36
- | [Gemini](adapters/gemini.md) | Google Gemini adapter |
37
-
38
27
  ## MCP (Model Context Protocol)
39
28
 
40
29
  Connect to external tool servers:
@@ -54,29 +43,19 @@ Real-time response streaming:
54
43
  | [Context](streaming/context.md) | Streaming context |
55
44
  | [Events](streaming/events.md) | Event utilities |
56
45
 
57
- ## History
58
-
59
- Conversation persistence:
60
-
61
- | Class | Description |
62
- |-------|-------------|
63
- | [Config](history/config.md) | History configuration |
64
- | [ThreadManager](history/thread-manager.md) | Thread lifecycle |
65
- | [ActiveRecordAdapter](history/active-record-adapter.md) | Rails adapter |
66
-
67
46
  ## Module Methods
68
47
 
69
48
  ### RobotLab
70
49
 
71
50
  ```ruby
72
51
  # Configuration
73
- RobotLab.configuration
74
- RobotLab.configure { |config| ... }
52
+ RobotLab.config # => Config instance
53
+ RobotLab.reload_config! # => reload from all sources
75
54
 
76
55
  # Building
77
- RobotLab.build { ... }
78
- RobotLab.create_network { ... }
79
- RobotLab.create_state(...)
56
+ RobotLab.build(name:, template:, system_prompt:, context:, **options)
57
+ RobotLab.create_network(name:, concurrency:) { ... }
58
+ RobotLab.create_memory(data:, enable_cache:, **options)
80
59
  ```
81
60
 
82
61
  See individual class documentation for detailed method references.