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,14 +1,20 @@
1
1
  # History::Config
2
2
 
3
- Configuration for conversation persistence.
3
+ Configuration for conversation history persistence using callback-based architecture.
4
4
 
5
5
  ## Class: `RobotLab::History::Config`
6
6
 
7
7
  ```ruby
8
- config = History::Config.new(
9
- create_thread: create_proc,
10
- get: get_proc,
11
- append_results: append_proc
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
+ }
12
18
  )
13
19
  ```
14
20
 
@@ -16,101 +22,217 @@ config = History::Config.new(
16
22
 
17
23
  ```ruby
18
24
  Config.new(
19
- create_thread:,
20
- get:,
21
- append_results:
25
+ create_thread: nil,
26
+ get: nil,
27
+ append_user_message: nil,
28
+ append_results: nil
22
29
  )
23
30
  ```
24
31
 
32
+ All parameters are optional Proc/lambda callbacks. The `configured?` method returns `true` only when both `create_thread` and `get` are set.
33
+
25
34
  **Parameters:**
26
35
 
27
- | Name | Type | Description |
28
- |------|------|-------------|
29
- | `create_thread` | `Proc` | Creates a new thread |
30
- | `get` | `Proc` | Retrieves thread history |
31
- | `append_results` | `Proc` | Appends results to thread |
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
32
44
 
33
- ## Callbacks
45
+ All attributes are read-write (`attr_accessor`):
34
46
 
35
47
  ### create_thread
36
48
 
37
- Called when a new conversation starts without a thread_id.
49
+ ```ruby
50
+ config.create_thread # => Proc | nil
51
+ config.create_thread = ->(state:, input:, **) { ... }
52
+ ```
53
+
54
+ ### get
38
55
 
39
56
  ```ruby
40
- create_thread: ->(state:, input:, **context) {
41
- # Create and return thread info
42
- { id: SecureRandom.uuid }
43
- }
57
+ config.get # => Proc | nil
58
+ config.get = ->(session_id:, **) { ... }
44
59
  ```
45
60
 
46
- **Arguments:**
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:**
47
94
 
48
95
  | Name | Type | Description |
49
96
  |------|------|-------------|
50
- | `state` | `State` | Current state |
51
- | `input` | `String` | User input |
52
- | `**context` | `Hash` | Additional context |
97
+ | `state` | `Object` | Current state or memory |
98
+ | `input` | `String`, `UserMessage` | Initial user input |
99
+ | `**kwargs` | `Hash` | Additional context passed through |
53
100
 
54
- **Returns:** Hash with `:id` key.
101
+ **Returns:** Hash with at least `{ session_id: "..." }`.
55
102
 
56
- ### get
103
+ **Raises:** `HistoryError` if callback is not configured or return value is invalid.
57
104
 
58
- Called to retrieve existing conversation history.
105
+ ### get!
59
106
 
60
107
  ```ruby
61
- get: ->(thread_id:, **context) {
62
- # Return array of previous results
63
- Thread.find(thread_id).results
64
- }
108
+ results = config.get!(session_id:, **kwargs)
65
109
  ```
66
110
 
67
- **Arguments:**
111
+ Invoke the `get` callback to retrieve history for a thread.
112
+
113
+ **Parameters:**
68
114
 
69
115
  | Name | Type | Description |
70
116
  |------|------|-------------|
71
- | `thread_id` | `String` | Thread identifier |
72
- | `**context` | `Hash` | Additional context |
117
+ | `session_id` | `String` | Thread identifier |
118
+ | `**kwargs` | `Hash` | Additional context |
73
119
 
74
- **Returns:** Array of `RobotResult` or hashes.
120
+ **Returns:** Array of `RobotResult` (or whatever the callback returns).
75
121
 
76
- ### append_results
122
+ **Raises:** `HistoryError` if callback is not configured.
77
123
 
78
- Called after each network run to persist new results.
124
+ ### append_user_message!
79
125
 
80
126
  ```ruby
81
- append_results: ->(thread_id:, new_results:, **context) {
82
- # Persist the new results
83
- thread = Thread.find(thread_id)
84
- new_results.each { |r| thread.results.create(r.to_h) }
85
- }
127
+ config.append_user_message!(session_id:, message:, **kwargs)
86
128
  ```
87
129
 
88
- **Arguments:**
130
+ Invoke the `append_user_message` callback. No-op if the callback is not configured.
131
+
132
+ **Parameters:**
89
133
 
90
134
  | Name | Type | Description |
91
135
  |------|------|-------------|
92
- | `thread_id` | `String` | Thread identifier |
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 |
93
153
  | `new_results` | `Array<RobotResult>` | Results to append |
94
- | `**context` | `Hash` | Additional context |
154
+ | `**kwargs` | `Hash` | Additional context |
95
155
 
96
- ## Attributes
156
+ ## Callback Signatures
97
157
 
98
158
  ### create_thread
99
159
 
160
+ Called when a new conversation starts.
161
+
100
162
  ```ruby
101
- config.create_thread # => Proc
163
+ create_thread: ->(state:, input:, **context) {
164
+ # Must return a Hash with :session_id
165
+ { session_id: SecureRandom.uuid }
166
+ }
102
167
  ```
103
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
+
104
175
  ### get
105
176
 
177
+ Called to retrieve existing conversation history.
178
+
106
179
  ```ruby
107
- config.get # => Proc
180
+ get: ->(session_id:, **context) {
181
+ # Return array of previous results
182
+ Thread.find(session_id).results
183
+ }
108
184
  ```
109
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
+
110
207
  ### append_results
111
208
 
209
+ Called after robot execution to persist results.
210
+
112
211
  ```ruby
113
- config.append_results # => Proc
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
114
236
  ```
115
237
 
116
238
  ## Examples
@@ -120,66 +242,37 @@ config.append_results # => Proc
120
242
  ```ruby
121
243
  STORE = {}
122
244
 
123
- config = History::Config.new(
245
+ config = RobotLab::History::Config.new(
124
246
  create_thread: ->(state:, **) {
125
247
  id = SecureRandom.uuid
126
248
  STORE[id] = { results: [] }
127
- { id: id }
249
+ { session_id: id }
128
250
  },
129
-
130
- get: ->(thread_id:, **) {
131
- STORE.dig(thread_id, :results) || []
251
+ get: ->(session_id:, **) {
252
+ STORE.dig(session_id, :results) || []
132
253
  },
133
-
134
- append_results: ->(thread_id:, new_results:, **) {
135
- STORE[thread_id][:results].concat(new_results.map(&:to_h))
254
+ append_results: ->(session_id:, new_results:, **) {
255
+ STORE[session_id][:results].concat(new_results.map(&:to_h))
136
256
  }
137
257
  )
138
258
  ```
139
259
 
140
- ### With Context
260
+ ### With User Scoping
141
261
 
142
262
  ```ruby
143
- config = History::Config.new(
263
+ config = RobotLab::History::Config.new(
144
264
  create_thread: ->(state:, user_id:, **) {
145
- Thread.create(user_id: user_id, started_at: Time.current)
265
+ thread = ConversationThread.create!(user_id: user_id)
266
+ { session_id: thread.session_id }
146
267
  },
147
-
148
- get: ->(thread_id:, user_id:, **) {
149
- Thread.where(id: thread_id, user_id: user_id).first&.results || []
268
+ get: ->(session_id:, user_id:, **) {
269
+ ConversationThread.where(session_id: session_id, user_id: user_id)
270
+ .first&.results || []
150
271
  },
151
-
152
- append_results: ->(thread_id:, new_results:, user_id:, **) {
153
- thread = Thread.find_by(id: thread_id, user_id: user_id)
272
+ append_results: ->(session_id:, new_results:, user_id:, **) {
273
+ thread = ConversationThread.find_by(session_id: session_id, user_id: user_id)
154
274
  return unless thread
155
- new_results.each { |r| thread.results.create(r.to_h) }
156
- }
157
- )
158
-
159
- # Pass context when running
160
- network.run(state: state, user_id: current_user.id)
161
- ```
162
-
163
- ### With Validation
164
-
165
- ```ruby
166
- config = History::Config.new(
167
- create_thread: ->(state:, **) {
168
- raise "Invalid state" unless state.data[:user_id]
169
- Thread.create(user_id: state.data[:user_id])
170
- },
171
-
172
- get: ->(thread_id:, **) {
173
- thread = Thread.find_by(id: thread_id)
174
- raise "Thread not found" unless thread
175
- thread.results
176
- },
177
-
178
- append_results: ->(thread_id:, new_results:, **) {
179
- thread = Thread.find(thread_id)
180
- Thread.transaction do
181
- new_results.each { |r| thread.results.create!(r.to_h) }
182
- end
275
+ new_results.each { |r| thread.results.create!(r.to_h) }
183
276
  }
184
277
  )
185
278
  ```
@@ -4,47 +4,49 @@ Conversation persistence and thread management.
4
4
 
5
5
  ## Overview
6
6
 
7
- The history system enables persistent conversations by storing and retrieving conversation threads and results.
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
8
 
9
9
  ```ruby
10
- network = RobotLab.create_network do
11
- name "persistent_chat"
12
-
13
- history History::Config.new(
14
- create_thread: ->(state:, **) { Thread.create(id: SecureRandom.uuid) },
15
- get: ->(thread_id:, **) { Thread.find(thread_id).results },
16
- append_results: ->(thread_id:, new_results:, **) {
17
- Thread.find(thread_id).results.concat(new_results)
18
- }
19
- )
20
-
21
- add_robot assistant
22
- end
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
+ )
23
22
  ```
24
23
 
25
24
  ## Components
26
25
 
27
26
  | Component | Description |
28
27
  |-----------|-------------|
29
- | [Config](config.md) | History configuration |
28
+ | [Config](config.md) | History configuration with persistence callbacks |
30
29
  | [ThreadManager](thread-manager.md) | Thread lifecycle management |
31
- | [ActiveRecordAdapter](active-record-adapter.md) | Rails integration |
30
+ | [ActiveRecordAdapter](active-record-adapter.md) | Rails ActiveRecord integration |
32
31
 
33
32
  ## Quick Start
34
33
 
35
34
  ### Basic Configuration
36
35
 
37
36
  ```ruby
38
- history = History::Config.new(
37
+ STORE = {}
38
+
39
+ history = RobotLab::History::Config.new(
39
40
  create_thread: ->(state:, input:, **) {
40
- { id: SecureRandom.uuid }
41
+ id = SecureRandom.uuid
42
+ STORE[id] = []
43
+ { session_id: id }
41
44
  },
42
- get: ->(thread_id:, **) {
43
- STORE[thread_id] || []
45
+ get: ->(session_id:, **) {
46
+ STORE[session_id] || []
44
47
  },
45
- append_results: ->(thread_id:, new_results:, **) {
46
- STORE[thread_id] ||= []
47
- STORE[thread_id].concat(new_results)
48
+ append_results: ->(session_id:, new_results:, **) {
49
+ STORE[session_id].concat(new_results)
48
50
  }
49
51
  )
50
52
  ```
@@ -52,36 +54,29 @@ history = History::Config.new(
52
54
  ### With ActiveRecord
53
55
 
54
56
  ```ruby
55
- history = History::ActiveRecordAdapter.new(
56
- thread_model: ConversationThread,
57
- result_model: ConversationResult
58
- ).to_config
57
+ adapter = RobotLab::History::ActiveRecordAdapter.new(
58
+ thread_model: RobotLabThread,
59
+ result_model: RobotLabResult
60
+ )
61
+
62
+ config = adapter.to_config
59
63
  ```
60
64
 
61
65
  ## Callbacks
62
66
 
63
- | Callback | Purpose |
64
- |----------|---------|
65
- | `create_thread` | Create new conversation thread |
66
- | `get` | Retrieve existing thread history |
67
- | `append_results` | Add results to thread |
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 |
68
73
 
69
74
  ## Thread Lifecycle
70
75
 
71
- ```mermaid
72
- sequenceDiagram
73
- participant U as User
74
- participant N as Network
75
- participant H as History
76
-
77
- U->>N: Run with new message
78
- N->>H: get(thread_id)
79
- H-->>N: Previous results
80
- N->>N: Execute robots
81
- N->>H: append_results(new_results)
82
- H-->>N: Saved
83
- N-->>U: Result with thread_id
84
- ```
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.
85
80
 
86
81
  ## Examples
87
82
 
@@ -90,17 +85,17 @@ sequenceDiagram
90
85
  ```ruby
91
86
  THREADS = {}
92
87
 
93
- history = History::Config.new(
88
+ history = RobotLab::History::Config.new(
94
89
  create_thread: ->(state:, **) {
95
90
  id = SecureRandom.uuid
96
91
  THREADS[id] = []
97
- { id: id }
92
+ { session_id: id }
98
93
  },
99
- get: ->(thread_id:, **) {
100
- THREADS[thread_id] || []
94
+ get: ->(session_id:, **) {
95
+ THREADS[session_id] || []
101
96
  },
102
- append_results: ->(thread_id:, new_results:, **) {
103
- THREADS[thread_id].concat(new_results)
97
+ append_results: ->(session_id:, new_results:, **) {
98
+ THREADS[session_id].concat(new_results)
104
99
  }
105
100
  )
106
101
  ```
@@ -108,25 +103,26 @@ history = History::Config.new(
108
103
  ### Redis Store
109
104
 
110
105
  ```ruby
111
- history = History::Config.new(
106
+ history = RobotLab::History::Config.new(
112
107
  create_thread: ->(state:, **) {
113
108
  id = SecureRandom.uuid
114
109
  Redis.current.set("thread:#{id}", [].to_json)
115
- { id: id }
110
+ { session_id: id }
116
111
  },
117
- get: ->(thread_id:, **) {
118
- data = Redis.current.get("thread:#{thread_id}")
112
+ get: ->(session_id:, **) {
113
+ data = Redis.current.get("thread:#{session_id}")
119
114
  data ? JSON.parse(data) : []
120
115
  },
121
- append_results: ->(thread_id:, new_results:, **) {
122
- existing = JSON.parse(Redis.current.get("thread:#{thread_id}") || "[]")
116
+ append_results: ->(session_id:, new_results:, **) {
117
+ existing = JSON.parse(Redis.current.get("thread:#{session_id}") || "[]")
123
118
  existing.concat(new_results.map(&:to_h))
124
- Redis.current.set("thread:#{thread_id}", existing.to_json)
119
+ Redis.current.set("thread:#{session_id}", existing.to_json)
125
120
  }
126
121
  )
127
122
  ```
128
123
 
129
124
  ## See Also
130
125
 
131
- - [History Guide](../../guides/history.md)
132
- - [State](../core/state.md)
126
+ - [Config](config.md)
127
+ - [ThreadManager](thread-manager.md)
128
+ - [ActiveRecordAdapter](active-record-adapter.md)