robot_lab 0.0.4 → 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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +64 -6
  4. data/Rakefile +2 -1
  5. data/docs/api/core/index.md +41 -46
  6. data/docs/api/core/memory.md +200 -154
  7. data/docs/api/core/network.md +13 -3
  8. data/docs/api/core/robot.md +38 -26
  9. data/docs/api/core/state.md +55 -73
  10. data/docs/api/index.md +7 -28
  11. data/docs/api/messages/index.md +35 -20
  12. data/docs/api/messages/text-message.md +67 -21
  13. data/docs/api/messages/tool-call-message.md +80 -41
  14. data/docs/api/messages/tool-result-message.md +119 -50
  15. data/docs/api/messages/user-message.md +48 -24
  16. data/docs/architecture/core-concepts.md +10 -15
  17. data/docs/concepts.md +5 -7
  18. data/docs/examples/index.md +2 -2
  19. data/docs/getting-started/configuration.md +80 -0
  20. data/docs/guides/building-robots.md +10 -9
  21. data/docs/guides/creating-networks.md +49 -0
  22. data/docs/guides/index.md +0 -5
  23. data/docs/guides/rails-integration.md +244 -162
  24. data/docs/guides/streaming.md +118 -138
  25. data/docs/index.md +0 -8
  26. data/examples/03_network.rb +10 -7
  27. data/examples/08_llm_config.rb +40 -11
  28. data/examples/09_chaining.rb +45 -6
  29. data/examples/11_network_introspection.rb +30 -7
  30. data/examples/12_message_bus.rb +1 -1
  31. data/examples/14_rusty_circuit/heckler.rb +14 -8
  32. data/examples/14_rusty_circuit/open_mic.rb +5 -3
  33. data/examples/14_rusty_circuit/scout.rb +14 -31
  34. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +1 -1
  35. data/examples/16_writers_room/display.rb +158 -0
  36. data/examples/16_writers_room/output/.gitignore +2 -0
  37. data/examples/16_writers_room/output/opus_001.md +263 -0
  38. data/examples/16_writers_room/output/opus_001_notes.log +470 -0
  39. data/examples/16_writers_room/prompts/writer.md +37 -0
  40. data/examples/16_writers_room/room.rb +150 -0
  41. data/examples/16_writers_room/tools.rb +162 -0
  42. data/examples/16_writers_room/writer.rb +121 -0
  43. data/examples/16_writers_room/writers_room.rb +162 -0
  44. data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
  45. data/lib/robot_lab/memory.rb +8 -32
  46. data/lib/robot_lab/network.rb +13 -20
  47. data/lib/robot_lab/robot/bus_messaging.rb +239 -0
  48. data/lib/robot_lab/robot/mcp_management.rb +88 -0
  49. data/lib/robot_lab/robot/template_rendering.rb +130 -0
  50. data/lib/robot_lab/robot.rb +56 -420
  51. data/lib/robot_lab/run_config.rb +184 -0
  52. data/lib/robot_lab/state_proxy.rb +2 -12
  53. data/lib/robot_lab/task.rb +8 -1
  54. data/lib/robot_lab/utils.rb +39 -0
  55. data/lib/robot_lab/version.rb +1 -1
  56. data/lib/robot_lab.rb +29 -8
  57. data/mkdocs.yml +0 -11
  58. metadata +15 -20
  59. data/docs/api/adapters/anthropic.md +0 -121
  60. data/docs/api/adapters/gemini.md +0 -133
  61. data/docs/api/adapters/index.md +0 -104
  62. data/docs/api/adapters/openai.md +0 -134
  63. data/docs/api/history/active-record-adapter.md +0 -275
  64. data/docs/api/history/config.md +0 -284
  65. data/docs/api/history/index.md +0 -128
  66. data/docs/api/history/thread-manager.md +0 -194
  67. data/docs/guides/history.md +0 -359
  68. data/lib/robot_lab/adapters/anthropic.rb +0 -163
  69. data/lib/robot_lab/adapters/base.rb +0 -85
  70. data/lib/robot_lab/adapters/gemini.rb +0 -193
  71. data/lib/robot_lab/adapters/openai.rb +0 -160
  72. data/lib/robot_lab/adapters/registry.rb +0 -81
  73. data/lib/robot_lab/errors.rb +0 -70
  74. data/lib/robot_lab/history/active_record_adapter.rb +0 -146
  75. data/lib/robot_lab/history/config.rb +0 -115
  76. data/lib/robot_lab/history/thread_manager.rb +0 -93
  77. data/lib/robot_lab/robotic_model.rb +0 -324
@@ -1,284 +0,0 @@
1
- # History::Config
2
-
3
- Configuration for conversation history persistence using callback-based architecture.
4
-
5
- ## Class: `RobotLab::History::Config`
6
-
7
- ```ruby
8
- config = RobotLab::History::Config.new(
9
- create_thread: ->(state:, input:, **) {
10
- { session_id: SecureRandom.uuid }
11
- },
12
- get: ->(session_id:, **) {
13
- database.find_results(session_id)
14
- },
15
- append_results: ->(session_id:, new_results:, **) {
16
- database.insert_results(session_id, new_results)
17
- }
18
- )
19
- ```
20
-
21
- ## Constructor
22
-
23
- ```ruby
24
- Config.new(
25
- create_thread: nil,
26
- get: nil,
27
- append_user_message: nil,
28
- append_results: nil
29
- )
30
- ```
31
-
32
- All parameters are optional Proc/lambda callbacks. The `configured?` method returns `true` only when both `create_thread` and `get` are set.
33
-
34
- **Parameters:**
35
-
36
- | Name | Type | Default | Description |
37
- |------|------|---------|-------------|
38
- | `create_thread` | `Proc`, `nil` | `nil` | Callback to create a new conversation thread |
39
- | `get` | `Proc`, `nil` | `nil` | Callback to retrieve history for a thread |
40
- | `append_user_message` | `Proc`, `nil` | `nil` | Callback to append user messages |
41
- | `append_results` | `Proc`, `nil` | `nil` | Callback to append robot results |
42
-
43
- ## Attributes
44
-
45
- All attributes are read-write (`attr_accessor`):
46
-
47
- ### create_thread
48
-
49
- ```ruby
50
- config.create_thread # => Proc | nil
51
- config.create_thread = ->(state:, input:, **) { ... }
52
- ```
53
-
54
- ### get
55
-
56
- ```ruby
57
- config.get # => Proc | nil
58
- config.get = ->(session_id:, **) { ... }
59
- ```
60
-
61
- ### append_user_message
62
-
63
- ```ruby
64
- config.append_user_message # => Proc | nil
65
- config.append_user_message = ->(session_id:, message:, **) { ... }
66
- ```
67
-
68
- ### append_results
69
-
70
- ```ruby
71
- config.append_results # => Proc | nil
72
- config.append_results = ->(session_id:, new_results:, **) { ... }
73
- ```
74
-
75
- ## Methods
76
-
77
- ### configured?
78
-
79
- ```ruby
80
- config.configured? # => Boolean
81
- ```
82
-
83
- Returns `true` if both `create_thread` and `get` callbacks are set.
84
-
85
- ### create_thread!
86
-
87
- ```ruby
88
- result = config.create_thread!(state:, input:, **kwargs)
89
- ```
90
-
91
- Invoke the `create_thread` callback. Validates that the callback is configured and that the return value is a Hash containing `:session_id`.
92
-
93
- **Parameters:**
94
-
95
- | Name | Type | Description |
96
- |------|------|-------------|
97
- | `state` | `Object` | Current state or memory |
98
- | `input` | `String`, `UserMessage` | Initial user input |
99
- | `**kwargs` | `Hash` | Additional context passed through |
100
-
101
- **Returns:** Hash with at least `{ session_id: "..." }`.
102
-
103
- **Raises:** `HistoryError` if callback is not configured or return value is invalid.
104
-
105
- ### get!
106
-
107
- ```ruby
108
- results = config.get!(session_id:, **kwargs)
109
- ```
110
-
111
- Invoke the `get` callback to retrieve history for a thread.
112
-
113
- **Parameters:**
114
-
115
- | Name | Type | Description |
116
- |------|------|-------------|
117
- | `session_id` | `String` | Thread identifier |
118
- | `**kwargs` | `Hash` | Additional context |
119
-
120
- **Returns:** Array of `RobotResult` (or whatever the callback returns).
121
-
122
- **Raises:** `HistoryError` if callback is not configured.
123
-
124
- ### append_user_message!
125
-
126
- ```ruby
127
- config.append_user_message!(session_id:, message:, **kwargs)
128
- ```
129
-
130
- Invoke the `append_user_message` callback. No-op if the callback is not configured.
131
-
132
- **Parameters:**
133
-
134
- | Name | Type | Description |
135
- |------|------|-------------|
136
- | `session_id` | `String` | Thread identifier |
137
- | `message` | `UserMessage` | User message to append |
138
- | `**kwargs` | `Hash` | Additional context |
139
-
140
- ### append_results!
141
-
142
- ```ruby
143
- config.append_results!(session_id:, new_results:, **kwargs)
144
- ```
145
-
146
- Invoke the `append_results` callback. No-op if the callback is not configured.
147
-
148
- **Parameters:**
149
-
150
- | Name | Type | Description |
151
- |------|------|-------------|
152
- | `session_id` | `String` | Thread identifier |
153
- | `new_results` | `Array<RobotResult>` | Results to append |
154
- | `**kwargs` | `Hash` | Additional context |
155
-
156
- ## Callback Signatures
157
-
158
- ### create_thread
159
-
160
- Called when a new conversation starts.
161
-
162
- ```ruby
163
- create_thread: ->(state:, input:, **context) {
164
- # Must return a Hash with :session_id
165
- { session_id: SecureRandom.uuid }
166
- }
167
- ```
168
-
169
- | Argument | Type | Description |
170
- |----------|------|-------------|
171
- | `state` | `Object` | Current robot memory or state |
172
- | `input` | `String`, `UserMessage` | Initial user input |
173
- | `**context` | `Hash` | Additional context |
174
-
175
- ### get
176
-
177
- Called to retrieve existing conversation history.
178
-
179
- ```ruby
180
- get: ->(session_id:, **context) {
181
- # Return array of previous results
182
- Thread.find(session_id).results
183
- }
184
- ```
185
-
186
- | Argument | Type | Description |
187
- |----------|------|-------------|
188
- | `session_id` | `String` | Thread identifier |
189
- | `**context` | `Hash` | Additional context |
190
-
191
- ### append_user_message
192
-
193
- Called to record user messages in the thread.
194
-
195
- ```ruby
196
- append_user_message: ->(session_id:, message:, **context) {
197
- Thread.find(session_id).update(last_message: message.content)
198
- }
199
- ```
200
-
201
- | Argument | Type | Description |
202
- |----------|------|-------------|
203
- | `session_id` | `String` | Thread identifier |
204
- | `message` | `UserMessage` | User message |
205
- | `**context` | `Hash` | Additional context |
206
-
207
- ### append_results
208
-
209
- Called after robot execution to persist results.
210
-
211
- ```ruby
212
- append_results: ->(session_id:, new_results:, **context) {
213
- new_results.each { |r| Thread.find(session_id).results.create(r.to_h) }
214
- }
215
- ```
216
-
217
- | Argument | Type | Description |
218
- |----------|------|-------------|
219
- | `session_id` | `String` | Thread identifier |
220
- | `new_results` | `Array<RobotResult>` | Results to append |
221
- | `**context` | `Hash` | Additional context |
222
-
223
- ## Error Handling
224
-
225
- History operations raise `RobotLab::History::HistoryError` (a subclass of `RobotLab::Error`) when:
226
-
227
- - A required callback is not configured and the `!` method is called
228
- - The `create_thread` callback returns a value without `:session_id`
229
-
230
- ```ruby
231
- begin
232
- config.create_thread!(state: memory, input: "Hello")
233
- rescue RobotLab::History::HistoryError => e
234
- puts "History error: #{e.message}"
235
- end
236
- ```
237
-
238
- ## Examples
239
-
240
- ### Basic Config
241
-
242
- ```ruby
243
- STORE = {}
244
-
245
- config = RobotLab::History::Config.new(
246
- create_thread: ->(state:, **) {
247
- id = SecureRandom.uuid
248
- STORE[id] = { results: [] }
249
- { session_id: id }
250
- },
251
- get: ->(session_id:, **) {
252
- STORE.dig(session_id, :results) || []
253
- },
254
- append_results: ->(session_id:, new_results:, **) {
255
- STORE[session_id][:results].concat(new_results.map(&:to_h))
256
- }
257
- )
258
- ```
259
-
260
- ### With User Scoping
261
-
262
- ```ruby
263
- config = RobotLab::History::Config.new(
264
- create_thread: ->(state:, user_id:, **) {
265
- thread = ConversationThread.create!(user_id: user_id)
266
- { session_id: thread.session_id }
267
- },
268
- get: ->(session_id:, user_id:, **) {
269
- ConversationThread.where(session_id: session_id, user_id: user_id)
270
- .first&.results || []
271
- },
272
- append_results: ->(session_id:, new_results:, user_id:, **) {
273
- thread = ConversationThread.find_by(session_id: session_id, user_id: user_id)
274
- return unless thread
275
- new_results.each { |r| thread.results.create!(r.to_h) }
276
- }
277
- )
278
- ```
279
-
280
- ## See Also
281
-
282
- - [History Overview](index.md)
283
- - [ThreadManager](thread-manager.md)
284
- - [ActiveRecordAdapter](active-record-adapter.md)
@@ -1,128 +0,0 @@
1
- # History
2
-
3
- Conversation persistence and thread management.
4
-
5
- ## Overview
6
-
7
- The history system enables persistent conversations by storing and retrieving conversation threads and results. It uses a callback-based architecture where you provide lambda/proc implementations for thread creation, retrieval, and result storage.
8
-
9
- ```ruby
10
- config = RobotLab::History::Config.new(
11
- create_thread: ->(state:, input:, **) {
12
- { session_id: SecureRandom.uuid }
13
- },
14
- get: ->(session_id:, **) {
15
- STORE[session_id] || []
16
- },
17
- append_results: ->(session_id:, new_results:, **) {
18
- STORE[session_id] ||= []
19
- STORE[session_id].concat(new_results)
20
- }
21
- )
22
- ```
23
-
24
- ## Components
25
-
26
- | Component | Description |
27
- |-----------|-------------|
28
- | [Config](config.md) | History configuration with persistence callbacks |
29
- | [ThreadManager](thread-manager.md) | Thread lifecycle management |
30
- | [ActiveRecordAdapter](active-record-adapter.md) | Rails ActiveRecord integration |
31
-
32
- ## Quick Start
33
-
34
- ### Basic Configuration
35
-
36
- ```ruby
37
- STORE = {}
38
-
39
- history = RobotLab::History::Config.new(
40
- create_thread: ->(state:, input:, **) {
41
- id = SecureRandom.uuid
42
- STORE[id] = []
43
- { session_id: id }
44
- },
45
- get: ->(session_id:, **) {
46
- STORE[session_id] || []
47
- },
48
- append_results: ->(session_id:, new_results:, **) {
49
- STORE[session_id].concat(new_results)
50
- }
51
- )
52
- ```
53
-
54
- ### With ActiveRecord
55
-
56
- ```ruby
57
- adapter = RobotLab::History::ActiveRecordAdapter.new(
58
- thread_model: RobotLabThread,
59
- result_model: RobotLabResult
60
- )
61
-
62
- config = adapter.to_config
63
- ```
64
-
65
- ## Callbacks
66
-
67
- | Callback | Required | Purpose |
68
- |----------|----------|---------|
69
- | `create_thread` | Yes (for `configured?`) | Create a new conversation thread |
70
- | `get` | Yes (for `configured?`) | Retrieve existing thread history |
71
- | `append_user_message` | No | Record user messages |
72
- | `append_results` | No | Persist robot results |
73
-
74
- ## Thread Lifecycle
75
-
76
- 1. **Create** -- When a new conversation starts, `create_thread` is called. It must return a Hash with a `:session_id` key.
77
- 2. **Retrieve** -- On subsequent messages, `get` is called with the `session_id` to load previous results.
78
- 3. **Append** -- After each robot execution, `append_results` is called with the new `RobotResult` objects.
79
- 4. **User Messages** -- Optionally, `append_user_message` records each user input.
80
-
81
- ## Examples
82
-
83
- ### In-Memory Store
84
-
85
- ```ruby
86
- THREADS = {}
87
-
88
- history = RobotLab::History::Config.new(
89
- create_thread: ->(state:, **) {
90
- id = SecureRandom.uuid
91
- THREADS[id] = []
92
- { session_id: id }
93
- },
94
- get: ->(session_id:, **) {
95
- THREADS[session_id] || []
96
- },
97
- append_results: ->(session_id:, new_results:, **) {
98
- THREADS[session_id].concat(new_results)
99
- }
100
- )
101
- ```
102
-
103
- ### Redis Store
104
-
105
- ```ruby
106
- history = RobotLab::History::Config.new(
107
- create_thread: ->(state:, **) {
108
- id = SecureRandom.uuid
109
- Redis.current.set("thread:#{id}", [].to_json)
110
- { session_id: id }
111
- },
112
- get: ->(session_id:, **) {
113
- data = Redis.current.get("thread:#{session_id}")
114
- data ? JSON.parse(data) : []
115
- },
116
- append_results: ->(session_id:, new_results:, **) {
117
- existing = JSON.parse(Redis.current.get("thread:#{session_id}") || "[]")
118
- existing.concat(new_results.map(&:to_h))
119
- Redis.current.set("thread:#{session_id}", existing.to_json)
120
- }
121
- )
122
- ```
123
-
124
- ## See Also
125
-
126
- - [Config](config.md)
127
- - [ThreadManager](thread-manager.md)
128
- - [ActiveRecordAdapter](active-record-adapter.md)
@@ -1,194 +0,0 @@
1
- # History::ThreadManager
2
-
3
- Manages conversation thread lifecycle using a `History::Config` for persistence.
4
-
5
- ## Class: `RobotLab::History::ThreadManager`
6
-
7
- ```ruby
8
- config = RobotLab::History::Config.new(...)
9
- manager = RobotLab::History::ThreadManager.new(config)
10
-
11
- session_id = manager.create_thread(state: memory, input: "Hello")
12
- history = manager.get_history(session_id)
13
- ```
14
-
15
- ## Constructor
16
-
17
- ```ruby
18
- ThreadManager.new(config)
19
- ```
20
-
21
- **Parameters:**
22
-
23
- | Name | Type | Description |
24
- |------|------|-------------|
25
- | `config` | `Config` | History configuration with persistence callbacks |
26
-
27
- ## Attributes
28
-
29
- ### config
30
-
31
- ```ruby
32
- manager.config # => RobotLab::History::Config
33
- ```
34
-
35
- The history configuration object.
36
-
37
- ## Methods
38
-
39
- ### create_thread
40
-
41
- ```ruby
42
- session_id = manager.create_thread(state:, input:)
43
- ```
44
-
45
- Create a new conversation thread. Delegates to `config.create_thread!` and returns the `session_id` from the result hash.
46
-
47
- **Parameters:**
48
-
49
- | Name | Type | Description |
50
- |------|------|-------------|
51
- | `state` | `Object` | Current robot memory or state |
52
- | `input` | `String`, `UserMessage` | Initial user input |
53
-
54
- **Returns:** `String` -- the session ID for the new thread.
55
-
56
- ### get_history
57
-
58
- ```ruby
59
- results = manager.get_history(session_id)
60
- ```
61
-
62
- Retrieve conversation history for a thread. Delegates to `config.get!`.
63
-
64
- **Parameters:**
65
-
66
- | Name | Type | Description |
67
- |------|------|-------------|
68
- | `session_id` | `String` | Thread identifier |
69
-
70
- **Returns:** `Array<RobotResult>` -- history of results for the thread.
71
-
72
- ### append_user_message
73
-
74
- ```ruby
75
- manager.append_user_message(session_id:, message:)
76
- ```
77
-
78
- Append a user message to the thread. Delegates to `config.append_user_message!`.
79
-
80
- **Parameters:**
81
-
82
- | Name | Type | Description |
83
- |------|------|-------------|
84
- | `session_id` | `String` | Thread identifier |
85
- | `message` | `UserMessage` | User message to append |
86
-
87
- ### append_results
88
-
89
- ```ruby
90
- manager.append_results(session_id:, results:)
91
- ```
92
-
93
- Append robot results to the thread. Delegates to `config.append_results!`.
94
-
95
- **Parameters:**
96
-
97
- | Name | Type | Description |
98
- |------|------|-------------|
99
- | `session_id` | `String` | Thread identifier |
100
- | `results` | `Array<RobotResult>` | Results to append |
101
-
102
- ### load_state
103
-
104
- ```ruby
105
- state = manager.load_state(session_id:, state:)
106
- ```
107
-
108
- Load history from a thread into a state/memory object. Retrieves the history, sets the `session_id` on the state, and calls `append_result` for each historical result.
109
-
110
- **Parameters:**
111
-
112
- | Name | Type | Description |
113
- |------|------|-------------|
114
- | `session_id` | `String` | Thread identifier |
115
- | `state` | `Object` | State or Memory object to populate |
116
-
117
- **Returns:** The state object with loaded history.
118
-
119
- ### save_state
120
-
121
- ```ruby
122
- manager.save_state(session_id:, state:, since_index: 0)
123
- ```
124
-
125
- Save new results from a state object to the thread. Extracts results from `state.results` starting at `since_index` and appends them.
126
-
127
- **Parameters:**
128
-
129
- | Name | Type | Description |
130
- |------|------|-------------|
131
- | `session_id` | `String` | Thread identifier |
132
- | `state` | `Object` | State object with results |
133
- | `since_index` | `Integer` | Save results from this index (default: 0) |
134
-
135
- ## Examples
136
-
137
- ### Basic Usage
138
-
139
- ```ruby
140
- config = RobotLab::History::Config.new(
141
- create_thread: ->(state:, input:, **) { { session_id: SecureRandom.uuid } },
142
- get: ->(session_id:, **) { STORE[session_id] || [] },
143
- append_results: ->(session_id:, new_results:, **) {
144
- STORE[session_id] ||= []
145
- STORE[session_id].concat(new_results)
146
- }
147
- )
148
-
149
- manager = RobotLab::History::ThreadManager.new(config)
150
-
151
- # Start a new conversation
152
- session_id = manager.create_thread(state: memory, input: "Hello")
153
-
154
- # Run a robot and save results
155
- result = robot.run("Hello")
156
- manager.append_results(session_id: session_id, results: [result])
157
-
158
- # Later, retrieve history
159
- history = manager.get_history(session_id)
160
- ```
161
-
162
- ### Loading History into Memory
163
-
164
- ```ruby
165
- manager = RobotLab::History::ThreadManager.new(config)
166
-
167
- # Create a memory object and load previous conversation
168
- memory = RobotLab::Memory.new
169
- manager.load_state(session_id: existing_session_id, state: memory)
170
-
171
- # Memory now contains previous results and session_id
172
- ```
173
-
174
- ### Saving Incremental Results
175
-
176
- ```ruby
177
- # Save only new results (since the last save point)
178
- initial_count = memory.results.length
179
-
180
- result = robot.run("Follow-up question")
181
- memory.append_result(result)
182
-
183
- manager.save_state(
184
- session_id: session_id,
185
- state: memory,
186
- since_index: initial_count
187
- )
188
- ```
189
-
190
- ## See Also
191
-
192
- - [History Overview](index.md)
193
- - [Config](config.md)
194
- - [ActiveRecordAdapter](active-record-adapter.md)