robot_lab 0.0.1 → 0.0.4

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 (145) 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 +90 -0
  5. data/README.md +203 -46
  6. data/Rakefile +70 -1
  7. data/docs/api/core/index.md +12 -0
  8. data/docs/api/core/robot.md +478 -130
  9. data/docs/api/core/tool.md +205 -209
  10. data/docs/api/history/active-record-adapter.md +174 -94
  11. data/docs/api/history/config.md +186 -93
  12. data/docs/api/history/index.md +57 -61
  13. data/docs/api/history/thread-manager.md +123 -73
  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/streaming/context.md +157 -74
  19. data/docs/api/streaming/events.md +114 -166
  20. data/docs/api/streaming/index.md +74 -72
  21. data/docs/architecture/core-concepts.md +361 -112
  22. data/docs/architecture/index.md +97 -59
  23. data/docs/architecture/message-flow.md +138 -129
  24. data/docs/architecture/network-orchestration.md +197 -50
  25. data/docs/architecture/robot-execution.md +199 -146
  26. data/docs/architecture/state-management.md +255 -187
  27. data/docs/concepts.md +312 -48
  28. data/docs/examples/basic-chat.md +89 -77
  29. data/docs/examples/index.md +222 -47
  30. data/docs/examples/mcp-server.md +207 -203
  31. data/docs/examples/multi-robot-network.md +129 -35
  32. data/docs/examples/rails-application.md +159 -160
  33. data/docs/examples/tool-usage.md +295 -204
  34. data/docs/getting-started/configuration.md +275 -162
  35. data/docs/getting-started/index.md +1 -1
  36. data/docs/getting-started/installation.md +22 -13
  37. data/docs/getting-started/quick-start.md +166 -121
  38. data/docs/guides/building-robots.md +417 -212
  39. data/docs/guides/creating-networks.md +94 -24
  40. data/docs/guides/mcp-integration.md +152 -113
  41. data/docs/guides/memory.md +220 -164
  42. data/docs/guides/streaming.md +80 -110
  43. data/docs/guides/using-tools.md +259 -212
  44. data/docs/index.md +50 -37
  45. data/examples/01_simple_robot.rb +6 -9
  46. data/examples/02_tools.rb +6 -9
  47. data/examples/03_network.rb +13 -14
  48. data/examples/04_mcp.rb +5 -8
  49. data/examples/05_streaming.rb +5 -8
  50. data/examples/06_prompt_templates.rb +42 -37
  51. data/examples/07_network_memory.rb +13 -14
  52. data/examples/08_llm_config.rb +140 -0
  53. data/examples/09_chaining.rb +223 -0
  54. data/examples/10_memory.rb +331 -0
  55. data/examples/11_network_introspection.rb +230 -0
  56. data/examples/12_message_bus.rb +74 -0
  57. data/examples/13_spawn.rb +90 -0
  58. data/examples/14_rusty_circuit/comic.rb +143 -0
  59. data/examples/14_rusty_circuit/display.rb +203 -0
  60. data/examples/14_rusty_circuit/heckler.rb +57 -0
  61. data/examples/14_rusty_circuit/open_mic.rb +121 -0
  62. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
  63. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
  64. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
  65. data/examples/14_rusty_circuit/scout.rb +173 -0
  66. data/examples/14_rusty_circuit/scout_notes.md +89 -0
  67. data/examples/14_rusty_circuit/show.log +234 -0
  68. data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
  69. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
  70. data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
  71. data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
  72. data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
  73. data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
  74. data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
  75. data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
  76. data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
  77. data/examples/15_memory_network_and_bus/output/memory.json +13 -0
  78. data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
  79. data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
  80. data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
  81. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
  82. data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
  83. data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
  84. data/examples/README.md +197 -0
  85. data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
  86. data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
  87. data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
  88. data/examples/prompts/comedian.md +6 -0
  89. data/examples/prompts/comedy_critic.md +10 -0
  90. data/examples/prompts/configurable.md +9 -0
  91. data/examples/prompts/dispatcher.md +12 -0
  92. data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
  93. data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
  94. data/examples/prompts/frontmatter_mcp_test.md +9 -0
  95. data/examples/prompts/frontmatter_named_test.md +5 -0
  96. data/examples/prompts/frontmatter_tools_test.md +6 -0
  97. data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
  98. data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
  99. data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
  100. data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
  101. data/examples/prompts/llm_config_demo.md +20 -0
  102. data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
  103. data/examples/prompts/os_advocate.md +13 -0
  104. data/examples/prompts/os_chief.md +13 -0
  105. data/examples/prompts/os_editor.md +13 -0
  106. data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
  107. data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
  108. data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
  109. data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
  110. data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
  111. data/lib/generators/robot_lab/templates/initializer.rb.tt +1 -1
  112. data/lib/robot_lab/adapters/openai.rb +2 -1
  113. data/lib/robot_lab/ask_user.rb +75 -0
  114. data/lib/robot_lab/config/defaults.yml +121 -0
  115. data/lib/robot_lab/config.rb +183 -0
  116. data/lib/robot_lab/error.rb +6 -0
  117. data/lib/robot_lab/mcp/client.rb +1 -1
  118. data/lib/robot_lab/memory.rb +2 -2
  119. data/lib/robot_lab/robot.rb +523 -249
  120. data/lib/robot_lab/robot_message.rb +44 -0
  121. data/lib/robot_lab/robot_result.rb +1 -0
  122. data/lib/robot_lab/robotic_model.rb +1 -1
  123. data/lib/robot_lab/streaming/context.rb +1 -1
  124. data/lib/robot_lab/tool.rb +108 -172
  125. data/lib/robot_lab/tool_config.rb +1 -1
  126. data/lib/robot_lab/tool_manifest.rb +2 -18
  127. data/lib/robot_lab/version.rb +1 -1
  128. data/lib/robot_lab.rb +66 -55
  129. metadata +107 -116
  130. data/examples/prompts/assistant/user.txt.erb +0 -1
  131. data/examples/prompts/billing/user.txt.erb +0 -1
  132. data/examples/prompts/classifier/user.txt.erb +0 -1
  133. data/examples/prompts/entity_extractor/user.txt.erb +0 -3
  134. data/examples/prompts/escalation/user.txt.erb +0 -34
  135. data/examples/prompts/general/user.txt.erb +0 -1
  136. data/examples/prompts/github_assistant/user.txt.erb +0 -1
  137. data/examples/prompts/helper/user.txt.erb +0 -1
  138. data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
  139. data/examples/prompts/order_support/user.txt.erb +0 -22
  140. data/examples/prompts/product_support/user.txt.erb +0 -32
  141. data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
  142. data/examples/prompts/synthesizer/user.txt.erb +0 -15
  143. data/examples/prompts/technical/user.txt.erb +0 -1
  144. data/examples/prompts/triage/user.txt.erb +0 -17
  145. data/lib/robot_lab/configuration.rb +0 -143
@@ -1,13 +1,15 @@
1
- # ActiveRecordAdapter
1
+ # History::ActiveRecordAdapter
2
2
 
3
- Rails ActiveRecord integration for conversation persistence.
3
+ ActiveRecord-based history persistence adapter for Rails applications.
4
4
 
5
5
  ## Class: `RobotLab::History::ActiveRecordAdapter`
6
6
 
7
+ Provides thread and result storage using ActiveRecord models. Converts itself to a `History::Config` via `to_config` for use with networks and thread managers.
8
+
7
9
  ```ruby
8
- adapter = History::ActiveRecordAdapter.new(
9
- thread_model: ConversationThread,
10
- result_model: ConversationResult
10
+ adapter = RobotLab::History::ActiveRecordAdapter.new(
11
+ thread_model: RobotLabThread,
12
+ result_model: RobotLabResult
11
13
  )
12
14
 
13
15
  config = adapter.to_config
@@ -16,75 +18,142 @@ config = adapter.to_config
16
18
  ## Constructor
17
19
 
18
20
  ```ruby
19
- ActiveRecordAdapter.new(
20
- thread_model:,
21
- result_model:,
22
- thread_factory: nil,
23
- result_factory: nil
24
- )
21
+ ActiveRecordAdapter.new(thread_model:, result_model:)
25
22
  ```
26
23
 
27
24
  **Parameters:**
28
25
 
29
26
  | Name | Type | Description |
30
27
  |------|------|-------------|
31
- | `thread_model` | `Class` | ActiveRecord model for threads |
32
- | `result_model` | `Class` | ActiveRecord model for results |
33
- | `thread_factory` | `Proc`, `nil` | Custom thread creation |
34
- | `result_factory` | `Proc`, `nil` | Custom result creation |
28
+ | `thread_model` | `Class` | ActiveRecord model class for conversation threads |
29
+ | `result_model` | `Class` | ActiveRecord model class for conversation results |
30
+
31
+ ## Attributes
32
+
33
+ ### thread_model
34
+
35
+ ```ruby
36
+ adapter.thread_model # => Class (ActiveRecord model)
37
+ ```
38
+
39
+ The ActiveRecord model class used for storing conversation threads.
40
+
41
+ ### result_model
42
+
43
+ ```ruby
44
+ adapter.result_model # => Class (ActiveRecord model)
45
+ ```
46
+
47
+ The ActiveRecord model class used for storing conversation results.
35
48
 
36
49
  ## Methods
37
50
 
38
51
  ### to_config
39
52
 
40
53
  ```ruby
41
- config = adapter.to_config
54
+ config = adapter.to_config # => RobotLab::History::Config
55
+ ```
56
+
57
+ Convert the adapter to a `History::Config` object. The config's callbacks delegate to the adapter's `create_thread`, `get`, `append_user_message`, and `append_results` methods.
58
+
59
+ ### create_thread
60
+
61
+ ```ruby
62
+ adapter.create_thread(state:, input:, **)
63
+ ```
64
+
65
+ Create a new conversation thread record. Generates a UUID `session_id`, extracts content and metadata from the input, and stores `state.data` as serialized state data.
66
+
67
+ **Parameters:**
68
+
69
+ | Name | Type | Description |
70
+ |------|------|-------------|
71
+ | `state` | `Object` | Current memory/state (must respond to `.data`) |
72
+ | `input` | `String`, `UserMessage` | Initial user input |
73
+
74
+ **Returns:** Hash with `{ session_id: "...", created_at: Time }`.
75
+
76
+ ### get
77
+
78
+ ```ruby
79
+ results = adapter.get(session_id:, **)
80
+ ```
81
+
82
+ Retrieve all results for a thread, ordered by `sequence_number` and `created_at`. Deserializes each record into a `RobotResult`.
83
+
84
+ **Parameters:**
85
+
86
+ | Name | Type | Description |
87
+ |------|------|-------------|
88
+ | `session_id` | `String` | Thread identifier |
89
+
90
+ **Returns:** `Array<RobotResult>` -- deserialized results.
91
+
92
+ ### append_user_message
93
+
94
+ ```ruby
95
+ adapter.append_user_message(session_id:, message:, **)
42
96
  ```
43
97
 
44
- Convert to `History::Config` for use with networks.
98
+ Update the thread record with the latest user message content and timestamp.
99
+
100
+ **Parameters:**
101
+
102
+ | Name | Type | Description |
103
+ |------|------|-------------|
104
+ | `session_id` | `String` | Thread identifier |
105
+ | `message` | `UserMessage` | User message |
106
+
107
+ ### append_results
108
+
109
+ ```ruby
110
+ adapter.append_results(session_id:, new_results:, **)
111
+ ```
112
+
113
+ Append robot results to the thread. Each result is stored with an auto-incrementing `sequence_number`. Serializes `output` and `tool_calls` from each `RobotResult`. Also updates the thread's `updated_at` timestamp.
114
+
115
+ **Parameters:**
116
+
117
+ | Name | Type | Description |
118
+ |------|------|-------------|
119
+ | `session_id` | `String` | Thread identifier |
120
+ | `new_results` | `Array<RobotResult>` | Results to append |
45
121
 
46
122
  ## Model Requirements
47
123
 
48
124
  ### Thread Model
49
125
 
126
+ The thread model must have the following columns:
127
+
50
128
  ```ruby
51
- # db/migrate/xxx_create_conversation_threads.rb
52
- create_table :conversation_threads do |t|
53
- t.string :external_id, null: false, index: { unique: true }
54
- t.jsonb :metadata, default: {}
129
+ # db/migrate/xxx_create_robot_lab_threads.rb
130
+ create_table :robot_lab_threads do |t|
131
+ t.string :session_id, null: false, index: { unique: true }
132
+ t.text :initial_input
133
+ t.jsonb :input_metadata, default: {}
134
+ t.jsonb :state_data, default: {}
135
+ t.text :last_user_message
136
+ t.datetime :last_user_message_at
55
137
  t.timestamps
56
138
  end
57
-
58
- # app/models/conversation_thread.rb
59
- class ConversationThread < ApplicationRecord
60
- has_many :results, class_name: "ConversationResult",
61
- foreign_key: :thread_id, dependent: :destroy
62
- end
63
139
  ```
64
140
 
65
141
  ### Result Model
66
142
 
143
+ The result model must have the following columns:
144
+
67
145
  ```ruby
68
- # db/migrate/xxx_create_conversation_results.rb
69
- create_table :conversation_results do |t|
70
- t.references :thread, foreign_key: { to_table: :conversation_threads }
146
+ # db/migrate/xxx_create_robot_lab_results.rb
147
+ create_table :robot_lab_results do |t|
148
+ t.string :session_id, null: false, index: true
71
149
  t.string :robot_name
72
- t.jsonb :input, default: {}
73
- t.jsonb :output, default: []
74
- t.jsonb :tool_calls, default: []
75
- t.jsonb :metadata, default: {}
76
- t.integer :position
150
+ t.integer :sequence_number
151
+ t.jsonb :output_data, default: []
152
+ t.jsonb :tool_calls_data, default: []
153
+ t.string :stop_reason
154
+ t.string :checksum
77
155
  t.timestamps
78
156
  end
79
-
80
- # app/models/conversation_result.rb
81
- class ConversationResult < ApplicationRecord
82
- belongs_to :thread, class_name: "ConversationThread"
83
-
84
- def to_robot_result
85
- RobotLab::RobotResult.from_hash(attributes)
86
- end
87
- end
88
157
  ```
89
158
 
90
159
  ## Examples
@@ -92,48 +161,52 @@ end
92
161
  ### Basic Setup
93
162
 
94
163
  ```ruby
95
- adapter = History::ActiveRecordAdapter.new(
96
- thread_model: ConversationThread,
97
- result_model: ConversationResult
164
+ adapter = RobotLab::History::ActiveRecordAdapter.new(
165
+ thread_model: RobotLabThread,
166
+ result_model: RobotLabResult
98
167
  )
99
168
 
100
- network = RobotLab.create_network do
101
- name "chat"
102
- history adapter.to_config
103
- add_robot assistant
104
- end
169
+ config = adapter.to_config
170
+ # Use config with a ThreadManager or pass to network configuration
105
171
  ```
106
172
 
107
- ### With Custom Factory
173
+ ### Using with ThreadManager
108
174
 
109
175
  ```ruby
110
- adapter = History::ActiveRecordAdapter.new(
111
- thread_model: ConversationThread,
112
- result_model: ConversationResult,
113
- thread_factory: ->(state:, input:, **context) {
114
- ConversationThread.create!(
115
- external_id: SecureRandom.uuid,
116
- user_id: context[:user_id],
117
- title: input.truncate(100),
118
- metadata: { source: context[:source] }
119
- )
120
- }
176
+ adapter = RobotLab::History::ActiveRecordAdapter.new(
177
+ thread_model: RobotLabThread,
178
+ result_model: RobotLabResult
121
179
  )
180
+
181
+ manager = RobotLab::History::ThreadManager.new(adapter.to_config)
182
+
183
+ # Create a thread
184
+ session_id = manager.create_thread(state: memory, input: "Hello")
185
+
186
+ # Run robot and save
187
+ result = robot.run("Hello")
188
+ manager.append_results(session_id: session_id, results: [result])
189
+
190
+ # Later, retrieve
191
+ history = manager.get_history(session_id)
122
192
  ```
123
193
 
124
- ### With User Scoping
194
+ ### With User-Scoped Models
195
+
196
+ For applications that need per-user thread scoping, create a custom adapter:
125
197
 
126
198
  ```ruby
127
- class ScopedAdapter
199
+ class ScopedHistoryAdapter
128
200
  def initialize(thread_model:, result_model:)
129
201
  @thread_model = thread_model
130
202
  @result_model = result_model
131
203
  end
132
204
 
133
205
  def to_config
134
- History::Config.new(
206
+ RobotLab::History::Config.new(
135
207
  create_thread: method(:create_thread),
136
208
  get: method(:get),
209
+ append_user_message: method(:append_user_message),
137
210
  append_results: method(:append_results)
138
211
  )
139
212
  end
@@ -141,55 +214,62 @@ class ScopedAdapter
141
214
  private
142
215
 
143
216
  def create_thread(state:, input:, user_id:, **)
144
- @thread_model.create!(
145
- external_id: SecureRandom.uuid,
217
+ thread = @thread_model.create!(
218
+ session_id: SecureRandom.uuid,
146
219
  user_id: user_id,
147
- title: input.truncate(100)
220
+ initial_input: input.to_s
148
221
  )
222
+ { session_id: thread.session_id, created_at: thread.created_at }
149
223
  end
150
224
 
151
- def get(thread_id:, user_id:, **)
152
- thread = @thread_model.find_by(external_id: thread_id, user_id: user_id)
153
- return [] unless thread
154
- thread.results.order(:position).map(&:to_robot_result)
225
+ def get(session_id:, user_id:, **)
226
+ @result_model
227
+ .joins(:thread)
228
+ .where(threads: { session_id: session_id, user_id: user_id })
229
+ .order(:sequence_number)
230
+ .map(&:to_robot_result)
155
231
  end
156
232
 
157
- def append_results(thread_id:, new_results:, user_id:, **)
158
- thread = @thread_model.find_by!(external_id: thread_id, user_id: user_id)
159
- position = thread.results.maximum(:position) || 0
160
-
161
- @result_model.transaction do
162
- new_results.each_with_index do |result, i|
163
- thread.results.create!(
164
- robot_name: result.robot_name,
165
- input: result.input.to_h,
166
- output: result.output.map(&:to_h),
167
- tool_calls: result.tool_calls.map(&:to_h),
168
- position: position + i + 1
169
- )
170
- end
233
+ def append_user_message(session_id:, message:, user_id:, **)
234
+ @thread_model.where(session_id: session_id, user_id: user_id)
235
+ .update_all(last_user_message: message.content, last_user_message_at: Time.current)
236
+ end
237
+
238
+ def append_results(session_id:, new_results:, user_id:, **)
239
+ base_seq = @result_model.where(session_id: session_id).maximum(:sequence_number) || 0
240
+
241
+ new_results.each_with_index do |result, i|
242
+ @result_model.create!(
243
+ session_id: session_id,
244
+ robot_name: result.robot_name,
245
+ sequence_number: base_seq + i + 1,
246
+ output_data: result.output.map(&:to_h),
247
+ tool_calls_data: result.tool_calls.map(&:to_h),
248
+ stop_reason: result.stop_reason,
249
+ checksum: result.checksum
250
+ )
171
251
  end
252
+
253
+ @thread_model.where(session_id: session_id).update_all(updated_at: Time.current)
172
254
  end
173
255
  end
174
256
  ```
175
257
 
176
258
  ### Rails Generator
177
259
 
178
- Use the Rails generator to create models:
260
+ Use the Rails generator to scaffold the required models and migrations:
179
261
 
180
262
  ```bash
181
263
  rails generate robot_lab:history
182
264
  ```
183
265
 
184
266
  This creates:
185
-
186
- - `ConversationThread` model
187
- - `ConversationResult` model
188
- - Database migrations
267
+ - Thread and result ActiveRecord models
268
+ - Database migrations with required columns
189
269
  - Initializer configuration
190
270
 
191
271
  ## See Also
192
272
 
193
273
  - [History Overview](index.md)
194
274
  - [Config](config.md)
195
- - [Rails Integration Guide](../../guides/rails-integration.md)
275
+ - [ThreadManager](thread-manager.md)