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
@@ -6,20 +6,35 @@ Tool invocation request from the LLM.
6
6
 
7
7
  ```ruby
8
8
  message = ToolCallMessage.new(
9
- id: "call_abc123",
10
- name: "get_weather",
11
- input: { city: "New York", units: "fahrenheit" }
9
+ role: "assistant",
10
+ tools: [
11
+ ToolMessage.new(id: "call_abc123", name: "get_weather", input: { city: "New York" })
12
+ ]
12
13
  )
13
14
  ```
14
15
 
15
16
  ## Constructor
16
17
 
17
18
  ```ruby
18
- ToolCallMessage.new(id:, name:, input:)
19
+ ToolCallMessage.new(role:, tools:, stop_reason: nil)
19
20
  ```
20
21
 
21
22
  **Parameters:**
22
23
 
24
+ | Name | Type | Description |
25
+ |------|------|-------------|
26
+ | `role` | `String` | Message role (typically "assistant") |
27
+ | `tools` | `Array<ToolMessage>` | Array of tool call objects |
28
+ | `stop_reason` | `String`, `nil` | Stop reason (defaults to "tool") |
29
+
30
+ ## ToolMessage
31
+
32
+ Each tool call is represented by a standalone `ToolMessage` object:
33
+
34
+ ```ruby
35
+ ToolMessage.new(id:, name:, input:)
36
+ ```
37
+
23
38
  | Name | Type | Description |
24
39
  |------|------|-------------|
25
40
  | `id` | `String` | Unique call identifier |
@@ -28,37 +43,45 @@ ToolCallMessage.new(id:, name:, input:)
28
43
 
29
44
  ## Attributes
30
45
 
31
- ### id
46
+ ### tools
32
47
 
33
48
  ```ruby
34
- message.id # => String
49
+ message.tools # => Array<ToolMessage>
35
50
  ```
36
51
 
37
- Unique identifier for this tool call. Used to match with `ToolResultMessage`.
52
+ Array of `ToolMessage` objects representing the tool calls.
38
53
 
39
- ### name
54
+ ### role
40
55
 
41
56
  ```ruby
42
- message.name # => String
57
+ message.role # => "assistant"
43
58
  ```
44
59
 
45
- Name of the tool being invoked.
60
+ Returns a String. The LLM initiates tool calls, so this is typically `"assistant"`.
46
61
 
47
- ### input
62
+ ### type
48
63
 
49
64
  ```ruby
50
- message.input # => Hash
65
+ message.type # => "tool_call"
51
66
  ```
52
67
 
53
- Parameters passed to the tool.
68
+ Always returns `"tool_call"`.
54
69
 
55
- ### role
70
+ ### content
71
+
72
+ ```ruby
73
+ message.content # => nil
74
+ ```
75
+
76
+ Always `nil` for tool call messages (the tool data is in `tools`).
77
+
78
+ ### stop_reason
56
79
 
57
80
  ```ruby
58
- message.role # => :assistant
81
+ message.stop_reason # => "tool"
59
82
  ```
60
83
 
61
- Always returns `:assistant` (the LLM initiates tool calls).
84
+ Defaults to `"tool"` indicating the conversation stopped for tool execution.
62
85
 
63
86
  ## Methods
64
87
 
@@ -74,12 +97,12 @@ Hash representation.
74
97
 
75
98
  ```ruby
76
99
  {
77
- role: :assistant,
78
- tool_call: {
79
- id: "call_abc123",
80
- name: "get_weather",
81
- input: { city: "New York", units: "fahrenheit" }
82
- }
100
+ type: "tool_call",
101
+ role: "assistant",
102
+ tools: [
103
+ { type: "tool", id: "call_abc123", name: "get_weather", input: { city: "New York" } }
104
+ ],
105
+ stop_reason: "tool"
83
106
  }
84
107
  ```
85
108
 
@@ -91,28 +114,48 @@ message.to_json # => String
91
114
 
92
115
  JSON representation.
93
116
 
117
+ ### Predicates
118
+
119
+ ```ruby
120
+ message.tool_call? # => true
121
+ message.text? # => false
122
+ message.assistant? # => true
123
+ message.tool_stop? # => true
124
+ ```
125
+
94
126
  ## Examples
95
127
 
96
- ### Basic Tool Call
128
+ ### Single Tool Call
97
129
 
98
130
  ```ruby
99
- call = ToolCallMessage.new(
131
+ tool = ToolMessage.new(
100
132
  id: "call_1",
101
133
  name: "search_orders",
102
134
  input: { user_id: "123", status: "pending" }
103
135
  )
136
+
137
+ call = ToolCallMessage.new(role: "assistant", tools: [tool])
138
+ ```
139
+
140
+ ### Multiple Tool Calls
141
+
142
+ ```ruby
143
+ tools = [
144
+ ToolMessage.new(id: "call_1", name: "get_weather", input: { city: "NYC" }),
145
+ ToolMessage.new(id: "call_2", name: "get_time", input: { timezone: "EST" })
146
+ ]
147
+
148
+ call = ToolCallMessage.new(role: "assistant", tools: tools)
149
+ call.tools.length # => 2
104
150
  ```
105
151
 
106
152
  ### Processing Tool Calls
107
153
 
108
154
  ```ruby
109
- result.output.each do |msg|
110
- case msg
111
- when ToolCallMessage
112
- puts "Tool called: #{msg.name}"
113
- puts "Parameters: #{msg.input.inspect}"
114
- when ToolResultMessage
115
- puts "Result for #{msg.id}: #{msg.result}"
155
+ if message.tool_call?
156
+ message.tools.each do |tool|
157
+ puts "Tool called: #{tool.name}"
158
+ puts "Parameters: #{tool.input.inspect}"
116
159
  end
117
160
  end
118
161
  ```
@@ -121,19 +164,15 @@ end
121
164
 
122
165
  ```ruby
123
166
  # LLM returns a tool call
124
- tool_call = ToolCallMessage.new(
125
- id: "call_weather_1",
126
- name: "get_weather",
127
- input: { city: "Seattle" }
128
- )
167
+ tool = ToolMessage.new(id: "call_weather_1", name: "get_weather", input: { city: "Seattle" })
168
+ tool_call = ToolCallMessage.new(role: "assistant", tools: [tool])
129
169
 
130
- # Tool is executed
131
- result = tool.call(tool_call.input, state: state)
170
+ # Execute the tool and record the result
171
+ result_data = execute_tool(tool.name, tool.input)
132
172
 
133
- # Result is recorded
134
173
  tool_result = ToolResultMessage.new(
135
- id: tool_call.id,
136
- result: result
174
+ tool: tool,
175
+ content: { data: result_data }
137
176
  )
138
177
  ```
139
178
 
@@ -5,53 +5,104 @@ Result from tool execution.
5
5
  ## Class: `RobotLab::ToolResultMessage`
6
6
 
7
7
  ```ruby
8
+ tool = ToolMessage.new(id: "call_abc123", name: "get_weather", input: { city: "NYC" })
9
+
8
10
  message = ToolResultMessage.new(
9
- id: "call_abc123",
10
- result: { temperature: 72, conditions: "sunny" }
11
+ tool: tool,
12
+ content: { data: { temperature: 72, conditions: "sunny" } }
11
13
  )
12
14
  ```
13
15
 
14
16
  ## Constructor
15
17
 
16
18
  ```ruby
17
- ToolResultMessage.new(id:, result:)
19
+ ToolResultMessage.new(tool:, content:, stop_reason: nil)
18
20
  ```
19
21
 
20
22
  **Parameters:**
21
23
 
22
24
  | Name | Type | Description |
23
25
  |------|------|-------------|
24
- | `id` | `String` | Matching tool call ID |
25
- | `result` | `Object` | Tool execution result |
26
+ | `tool` | `ToolMessage` | The tool call that was executed |
27
+ | `content` | `Hash` | Result with `:data` key (success) or `:error` key (failure) |
28
+ | `stop_reason` | `String`, `nil` | Stop reason (defaults to "tool") |
26
29
 
27
30
  ## Attributes
28
31
 
29
- ### id
32
+ ### tool
30
33
 
31
34
  ```ruby
32
- message.id # => String
35
+ message.tool # => ToolMessage
33
36
  ```
34
37
 
35
- Identifier matching the corresponding `ToolCallMessage`.
38
+ The `ToolMessage` representing the tool call that produced this result. Provides access to `tool.id`, `tool.name`, and `tool.input`.
36
39
 
37
- ### result
40
+ ### content
38
41
 
39
42
  ```ruby
40
- message.result # => Object
43
+ message.content # => Hash
41
44
  ```
42
45
 
43
- The result returned by the tool. Can be any serializable object.
46
+ The result content. Contains either a `:data` key (success) or an `:error` key (failure).
44
47
 
45
48
  ### role
46
49
 
47
50
  ```ruby
48
- message.role # => :tool
51
+ message.role # => "tool_result"
52
+ ```
53
+
54
+ Always returns `"tool_result"`.
55
+
56
+ ### type
57
+
58
+ ```ruby
59
+ message.type # => "tool_result"
49
60
  ```
50
61
 
51
- Always returns `:tool`.
62
+ Always returns `"tool_result"`.
63
+
64
+ ### stop_reason
65
+
66
+ ```ruby
67
+ message.stop_reason # => "tool"
68
+ ```
69
+
70
+ Defaults to `"tool"`.
52
71
 
53
72
  ## Methods
54
73
 
74
+ ### success?
75
+
76
+ ```ruby
77
+ message.success? # => Boolean
78
+ ```
79
+
80
+ Returns `true` if the content contains a `:data` key.
81
+
82
+ ### error?
83
+
84
+ ```ruby
85
+ message.error? # => Boolean
86
+ ```
87
+
88
+ Returns `true` if the content contains an `:error` key.
89
+
90
+ ### data
91
+
92
+ ```ruby
93
+ message.data # => Object | nil
94
+ ```
95
+
96
+ Returns the result data if successful, `nil` otherwise.
97
+
98
+ ### error
99
+
100
+ ```ruby
101
+ message.error # => String | nil
102
+ ```
103
+
104
+ Returns the error message if there was an error, `nil` otherwise.
105
+
55
106
  ### to_h
56
107
 
57
108
  ```ruby
@@ -64,11 +115,11 @@ Hash representation.
64
115
 
65
116
  ```ruby
66
117
  {
67
- role: :tool,
68
- tool_result: {
69
- id: "call_abc123",
70
- result: { temperature: 72, conditions: "sunny" }
71
- }
118
+ type: "tool_result",
119
+ role: "tool_result",
120
+ tool: { type: "tool", id: "call_abc123", name: "get_weather", input: { city: "NYC" } },
121
+ content: { data: { temperature: 72, conditions: "sunny" } },
122
+ stop_reason: "tool"
72
123
  }
73
124
  ```
74
125
 
@@ -80,70 +131,88 @@ message.to_json # => String
80
131
 
81
132
  JSON representation.
82
133
 
83
- ## Examples
84
-
85
- ### Basic Result
134
+ ### Predicates
86
135
 
87
136
  ```ruby
88
- result = ToolResultMessage.new(
89
- id: "call_1",
90
- result: { success: true, order_id: "ord_123" }
91
- )
137
+ message.tool_result? # => true
138
+ message.tool_call? # => false
139
+ message.text? # => false
140
+ message.tool_stop? # => true
92
141
  ```
93
142
 
94
- ### String Result
143
+ ## Examples
144
+
145
+ ### Successful Result
95
146
 
96
147
  ```ruby
148
+ tool = ToolMessage.new(id: "call_1", name: "search_orders", input: { user_id: "123" })
149
+
97
150
  result = ToolResultMessage.new(
98
- id: "call_time",
99
- result: "2024-01-15T10:30:00Z"
151
+ tool: tool,
152
+ content: { data: { order_id: "ord_123", status: "shipped" } }
100
153
  )
154
+
155
+ result.success? # => true
156
+ result.data # => { order_id: "ord_123", status: "shipped" }
101
157
  ```
102
158
 
103
- ### Array Result
159
+ ### Error Result
104
160
 
105
161
  ```ruby
162
+ tool = ToolMessage.new(id: "call_order", name: "get_order", input: { id: "bad" })
163
+
106
164
  result = ToolResultMessage.new(
107
- id: "call_search",
108
- result: [
109
- { id: 1, name: "Product A" },
110
- { id: 2, name: "Product B" }
111
- ]
165
+ tool: tool,
166
+ content: { error: "Order not found" }
112
167
  )
168
+
169
+ result.error? # => true
170
+ result.error # => "Order not found"
171
+ result.data # => nil
113
172
  ```
114
173
 
115
- ### Error Result
174
+ ### Accessing Tool Information
116
175
 
117
176
  ```ruby
118
177
  result = ToolResultMessage.new(
119
- id: "call_order",
120
- result: { success: false, error: "Order not found" }
178
+ tool: ToolMessage.new(id: "call_1", name: "get_weather", input: { city: "Berlin" }),
179
+ content: { data: { temperature: 15, unit: "celsius" } }
121
180
  )
181
+
182
+ result.tool.name # => "get_weather"
183
+ result.tool.id # => "call_1"
184
+ result.tool.input # => { city: "Berlin" }
185
+ result.data # => { temperature: 15, unit: "celsius" }
122
186
  ```
123
187
 
124
- ### Matching with Tool Calls
188
+ ### Matching Tool Calls with Results
125
189
 
126
190
  ```ruby
127
- # Process all tool interactions
128
- result.output.each_cons(2) do |a, b|
129
- if a.is_a?(ToolCallMessage) && b.is_a?(ToolResultMessage)
130
- if a.id == b.id
131
- puts "#{a.name}(#{a.input}) => #{b.result}"
132
- end
191
+ # Given a ToolCallMessage and its results
192
+ tool_call_msg.tools.each do |tool|
193
+ # Find the matching result
194
+ matching_result = results.find { |r| r.tool.id == tool.id }
195
+
196
+ if matching_result&.success?
197
+ puts "#{tool.name}(#{tool.input}) => #{matching_result.data}"
198
+ elsif matching_result&.error?
199
+ puts "#{tool.name} failed: #{matching_result.error}"
133
200
  end
134
201
  end
135
202
  ```
136
203
 
137
- ### In Result History
204
+ ### In Memory History
138
205
 
139
206
  ```ruby
140
- # Find all tool results from execution
141
- tool_results = state.results
142
- .flat_map(&:output)
143
- .select { |m| m.is_a?(ToolResultMessage) }
207
+ # Find all tool results from memory
208
+ tool_results = memory.messages.select(&:tool_result?)
144
209
 
145
210
  tool_results.each do |tr|
146
- puts "Tool result: #{tr.result}"
211
+ if tr.success?
212
+ puts "#{tr.tool.name}: #{tr.data}"
213
+ else
214
+ puts "#{tr.tool.name} error: #{tr.error}"
215
+ end
147
216
  end
148
217
  ```
149
218
 
@@ -7,16 +7,18 @@ User input with conversation metadata.
7
7
  ```ruby
8
8
  message = UserMessage.new(
9
9
  "What's my order status?",
10
- thread_id: "thread_123",
10
+ session_id: "session_123",
11
11
  system_prompt: "Be concise",
12
12
  metadata: { source: "web" }
13
13
  )
14
14
  ```
15
15
 
16
+ **Note:** `UserMessage` is a standalone class, not a subclass of `Message`.
17
+
16
18
  ## Constructor
17
19
 
18
20
  ```ruby
19
- UserMessage.new(content, thread_id: nil, system_prompt: nil, metadata: {})
21
+ UserMessage.new(content, session_id: nil, system_prompt: nil, metadata: nil, id: nil)
20
22
  ```
21
23
 
22
24
  **Parameters:**
@@ -24,9 +26,10 @@ UserMessage.new(content, thread_id: nil, system_prompt: nil, metadata: {})
24
26
  | Name | Type | Description |
25
27
  |------|------|-------------|
26
28
  | `content` | `String` | Message text |
27
- | `thread_id` | `String`, `nil` | Conversation thread ID |
29
+ | `session_id` | `String`, `nil` | Conversation session ID |
28
30
  | `system_prompt` | `String`, `nil` | Override system prompt |
29
- | `metadata` | `Hash` | Additional metadata |
31
+ | `metadata` | `Hash`, `nil` | Additional metadata |
32
+ | `id` | `String`, `nil` | Unique message ID (defaults to UUID) |
30
33
 
31
34
  ## Attributes
32
35
 
@@ -38,13 +41,13 @@ message.content # => String
38
41
 
39
42
  The message text.
40
43
 
41
- ### thread_id
44
+ ### session_id
42
45
 
43
46
  ```ruby
44
- message.thread_id # => String | nil
47
+ message.session_id # => String | nil
45
48
  ```
46
49
 
47
- Conversation thread identifier for history persistence.
50
+ Conversation session identifier for history persistence.
48
51
 
49
52
  ### system_prompt
50
53
 
@@ -78,14 +81,6 @@ message.created_at # => Time
78
81
 
79
82
  Message creation timestamp.
80
83
 
81
- ### role
82
-
83
- ```ruby
84
- message.role # => :user
85
- ```
86
-
87
- Always returns `:user`.
88
-
89
84
  ## Methods
90
85
 
91
86
  ### to_h
@@ -100,10 +95,11 @@ Hash representation.
100
95
 
101
96
  ```ruby
102
97
  {
103
- role: :user,
104
98
  content: "What's my order status?",
99
+ session_id: "session_123",
100
+ system_prompt: "Be concise",
101
+ metadata: { source: "web" },
105
102
  id: "uuid-here",
106
- thread_id: "thread_123",
107
103
  created_at: "2024-01-15T10:30:00Z"
108
104
  }
109
105
  ```
@@ -116,6 +112,30 @@ message.to_json # => String
116
112
 
117
113
  JSON representation.
118
114
 
115
+ ### to_message
116
+
117
+ ```ruby
118
+ message.to_message # => TextMessage
119
+ ```
120
+
121
+ Converts to a `TextMessage` with role `"user"` for use in conversation history.
122
+
123
+ ### to_s
124
+
125
+ ```ruby
126
+ message.to_s # => String
127
+ ```
128
+
129
+ Returns the content string.
130
+
131
+ ### self.from
132
+
133
+ ```ruby
134
+ UserMessage.from(input) # => UserMessage
135
+ ```
136
+
137
+ Creates a `UserMessage` from a String, Hash, or existing `UserMessage`.
138
+
119
139
  ## Examples
120
140
 
121
141
  ### Basic Message
@@ -124,12 +144,12 @@ JSON representation.
124
144
  message = UserMessage.new("Hello!")
125
145
  ```
126
146
 
127
- ### With Thread ID
147
+ ### With Session ID
128
148
 
129
149
  ```ruby
130
150
  message = UserMessage.new(
131
151
  "Continue our conversation",
132
- thread_id: "thread_abc123"
152
+ session_id: "session_abc123"
133
153
  )
134
154
  ```
135
155
 
@@ -156,16 +176,20 @@ message = UserMessage.new(
156
176
  )
157
177
  ```
158
178
 
159
- ### Creating State
179
+ ### Creating from Various Inputs
160
180
 
161
181
  ```ruby
162
- message = UserMessage.new("Help", thread_id: "thread_123")
163
- state = RobotLab.create_state(message: message)
182
+ # From a string
183
+ msg = UserMessage.from("Hello!")
184
+
185
+ # From a hash
186
+ msg = UserMessage.from(content: "Hello!", session_id: "123")
164
187
 
165
- state.thread_id # => "thread_123"
188
+ # From an existing UserMessage (returns as-is)
189
+ msg = UserMessage.from(existing_message)
166
190
  ```
167
191
 
168
192
  ## See Also
169
193
 
170
- - [State](../core/state.md)
194
+ - [Memory](../core/memory.md)
171
195
  - [TextMessage](text-message.md)