robot_lab 0.3.0 → 0.5.0

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 (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/README.md +29 -19
  4. data/lib/robot_lab/errors.rb +4 -0
  5. data/lib/robot_lab/hook_context.rb +9 -2
  6. data/lib/robot_lab/mcp/registry.rb +99 -0
  7. data/lib/robot_lab/mcp/server.rb +47 -83
  8. data/lib/robot_lab/mcp/server_discovery.rb +22 -9
  9. data/lib/robot_lab/mcp.rb +259 -0
  10. data/lib/robot_lab/robot/mcp_management.rb +159 -105
  11. data/lib/robot_lab/robot/tool_call_gating.rb +144 -0
  12. data/lib/robot_lab/robot.rb +72 -99
  13. data/lib/robot_lab/run_config.rb +3 -2
  14. data/lib/robot_lab/tool.rb +8 -23
  15. data/lib/robot_lab/version.rb +1 -1
  16. data/lib/robot_lab.rb +1 -4
  17. metadata +16 -292
  18. data/.architecture/AGENTS.md +0 -32
  19. data/.architecture/config.yml +0 -8
  20. data/.architecture/members.yml +0 -60
  21. data/.architecture/reviews/feature-free-will.md +0 -490
  22. data/.architecture/reviews/overall-codebase.md +0 -427
  23. data/.claude/settings.local.json +0 -57
  24. data/.codex/config.toml +0 -2
  25. data/.envrc +0 -6
  26. data/.github/workflows/deploy-github-pages.yml +0 -52
  27. data/.irbrc +0 -6
  28. data/.loki +0 -65
  29. data/.quality/reek_baseline.txt +0 -43
  30. data/.rubocop.yml +0 -10
  31. data/Archspec.rb +0 -44
  32. data/CLAUDE.md +0 -141
  33. data/COMMITS.md +0 -196
  34. data/Rakefile +0 -193
  35. data/_typos.toml +0 -21
  36. data/agent2agent_review.md +0 -192
  37. data/agentf_improvements.md +0 -253
  38. data/agents.md +0 -14
  39. data/docs/api/core/index.md +0 -147
  40. data/docs/api/core/memory.md +0 -578
  41. data/docs/api/core/network.md +0 -553
  42. data/docs/api/core/result.md +0 -211
  43. data/docs/api/core/robot.md +0 -1667
  44. data/docs/api/core/state.md +0 -145
  45. data/docs/api/core/tool.md +0 -520
  46. data/docs/api/errors.md +0 -323
  47. data/docs/api/hooks.md +0 -469
  48. data/docs/api/index.md +0 -137
  49. data/docs/api/mcp/client.md +0 -387
  50. data/docs/api/mcp/index.md +0 -267
  51. data/docs/api/mcp/server.md +0 -252
  52. data/docs/api/mcp/transports.md +0 -339
  53. data/docs/api/messages/index.md +0 -126
  54. data/docs/api/messages/text-message.md +0 -170
  55. data/docs/api/messages/tool-call-message.md +0 -206
  56. data/docs/api/messages/tool-result-message.md +0 -242
  57. data/docs/api/messages/user-message.md +0 -232
  58. data/docs/api/skills.md +0 -423
  59. data/docs/api/streaming/context.md +0 -280
  60. data/docs/api/streaming/events.md +0 -185
  61. data/docs/api/streaming/index.md +0 -156
  62. data/docs/api/support.md +0 -846
  63. data/docs/architecture/core-concepts.md +0 -575
  64. data/docs/architecture/index.md +0 -252
  65. data/docs/architecture/message-flow.md +0 -366
  66. data/docs/architecture/network-orchestration.md +0 -478
  67. data/docs/architecture/robot-execution.md +0 -396
  68. data/docs/architecture/state-management.md +0 -410
  69. data/docs/assets/css/custom.css +0 -56
  70. data/docs/assets/images/robot_lab.jpg +0 -0
  71. data/docs/concepts.md +0 -641
  72. data/docs/examples/basic-chat.md +0 -258
  73. data/docs/examples/index.md +0 -425
  74. data/docs/examples/mcp-server.md +0 -403
  75. data/docs/examples/multi-robot-network.md +0 -476
  76. data/docs/examples/tool-usage.md +0 -468
  77. data/docs/getting-started/configuration.md +0 -645
  78. data/docs/getting-started/index.md +0 -56
  79. data/docs/getting-started/installation.md +0 -197
  80. data/docs/getting-started/quick-start.md +0 -293
  81. data/docs/guides/building-robots.md +0 -1187
  82. data/docs/guides/creating-networks.md +0 -736
  83. data/docs/guides/hooks.md +0 -1206
  84. data/docs/guides/index.md +0 -78
  85. data/docs/guides/knowledge.md +0 -219
  86. data/docs/guides/mcp-integration.md +0 -740
  87. data/docs/guides/memory.md +0 -458
  88. data/docs/guides/observability.md +0 -733
  89. data/docs/guides/streaming.md +0 -323
  90. data/docs/guides/using-tools.md +0 -780
  91. data/docs/index.md +0 -280
  92. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  93. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  94. data/examples/.envrc +0 -3
  95. data/examples/01_simple_robot.rb +0 -31
  96. data/examples/02_tools.rb +0 -109
  97. data/examples/03_network.rb +0 -118
  98. data/examples/04_mcp.rb +0 -217
  99. data/examples/05_streaming.rb +0 -137
  100. data/examples/06_prompt_templates.rb +0 -325
  101. data/examples/07_network_memory.rb +0 -313
  102. data/examples/08_llm_config.rb +0 -162
  103. data/examples/09_chaining.rb +0 -251
  104. data/examples/10_memory.rb +0 -315
  105. data/examples/11_network_introspection.rb +0 -239
  106. data/examples/12_message_bus.rb +0 -67
  107. data/examples/13_spawn.rb +0 -85
  108. data/examples/14_rusty_circuit/.envrc +0 -1
  109. data/examples/14_rusty_circuit/.gitignore +0 -1
  110. data/examples/14_rusty_circuit/README.md +0 -1
  111. data/examples/14_rusty_circuit/comic.rb +0 -149
  112. data/examples/14_rusty_circuit/display.rb +0 -217
  113. data/examples/14_rusty_circuit/heckler.rb +0 -65
  114. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  115. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  116. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  117. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  118. data/examples/14_rusty_circuit/scout.rb +0 -165
  119. data/examples/14_rusty_circuit/show.log +0 -234
  120. data/examples/15_memory_network_and_bus/.envrc +0 -1
  121. data/examples/15_memory_network_and_bus/README.md +0 -66
  122. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  123. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  124. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  125. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  126. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  127. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  128. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  129. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  130. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  131. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  132. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  133. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  134. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  135. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  136. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  137. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  138. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  139. data/examples/16_writers_room/.envrc +0 -1
  140. data/examples/16_writers_room/display.rb +0 -158
  141. data/examples/16_writers_room/output/.gitignore +0 -4
  142. data/examples/16_writers_room/output/README.md +0 -69
  143. data/examples/16_writers_room/output/opus_001.md +0 -263
  144. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  145. data/examples/16_writers_room/output/opus_002.md +0 -245
  146. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  147. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  148. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  149. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  150. data/examples/16_writers_room/prompts/writer.md +0 -37
  151. data/examples/16_writers_room/room.rb +0 -186
  152. data/examples/16_writers_room/tools.rb +0 -173
  153. data/examples/16_writers_room/writer.rb +0 -121
  154. data/examples/16_writers_room/writers_room.rb +0 -256
  155. data/examples/17_skills.rb +0 -234
  156. data/examples/18_rails/.envrc +0 -3
  157. data/examples/18_rails/.gitignore +0 -5
  158. data/examples/18_rails/Gemfile +0 -11
  159. data/examples/18_rails/README.md +0 -67
  160. data/examples/18_rails/Rakefile +0 -4
  161. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  162. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  163. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  164. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  165. data/examples/18_rails/app/models/application_record.rb +0 -5
  166. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  167. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  168. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  169. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  170. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  171. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  172. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  173. data/examples/18_rails/bin/dev +0 -7
  174. data/examples/18_rails/bin/rails +0 -6
  175. data/examples/18_rails/bin/setup +0 -15
  176. data/examples/18_rails/config/application.rb +0 -33
  177. data/examples/18_rails/config/cable.yml +0 -2
  178. data/examples/18_rails/config/database.yml +0 -5
  179. data/examples/18_rails/config/environment.rb +0 -4
  180. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  181. data/examples/18_rails/config/routes.rb +0 -6
  182. data/examples/18_rails/config.ru +0 -4
  183. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  184. data/examples/19_token_tracking.rb +0 -138
  185. data/examples/20_circuit_breaker.rb +0 -147
  186. data/examples/21_learning_loop.rb +0 -181
  187. data/examples/22_context_compression.rb +0 -172
  188. data/examples/23_convergence.rb +0 -133
  189. data/examples/24_structured_delegation.rb +0 -151
  190. data/examples/25_history_search/conversation.jsonl +0 -30
  191. data/examples/25_history_search.rb +0 -133
  192. data/examples/26_document_store/api_versioning_adr.md +0 -52
  193. data/examples/26_document_store/incident_postmortem.md +0 -46
  194. data/examples/26_document_store/postgres_runbook.md +0 -49
  195. data/examples/26_document_store/redis_caching_guide.md +0 -48
  196. data/examples/26_document_store/sidekiq_guide.md +0 -51
  197. data/examples/26_document_store.rb +0 -140
  198. data/examples/27_incident_response/README.md +0 -65
  199. data/examples/27_incident_response/incident_response.rb +0 -261
  200. data/examples/28_mcp_discovery.rb +0 -113
  201. data/examples/29_ractor_tools.rb +0 -240
  202. data/examples/30_ractor_network.rb +0 -252
  203. data/examples/31_launch_assessment.rb +0 -246
  204. data/examples/32_newsletter_reader.rb +0 -193
  205. data/examples/33_stock_generator.rb +0 -80
  206. data/examples/33_stock_predictor.rb +0 -327
  207. data/examples/34_agentskills.rb +0 -76
  208. data/examples/35_hooks.rb +0 -266
  209. data/examples/README.md +0 -550
  210. data/examples/common.rb +0 -180
  211. data/examples/prompts/assistant.md +0 -5
  212. data/examples/prompts/audit_trail.md +0 -8
  213. data/examples/prompts/billing.md +0 -10
  214. data/examples/prompts/classifier.md +0 -7
  215. data/examples/prompts/comedian.md +0 -6
  216. data/examples/prompts/comedy_critic.md +0 -10
  217. data/examples/prompts/configurable.md +0 -9
  218. data/examples/prompts/dispatcher.md +0 -12
  219. data/examples/prompts/entity_extractor.md +0 -14
  220. data/examples/prompts/escalation.md +0 -42
  221. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  222. data/examples/prompts/frontmatter_named_test.md +0 -5
  223. data/examples/prompts/frontmatter_tools_test.md +0 -6
  224. data/examples/prompts/general.md +0 -7
  225. data/examples/prompts/github_assistant.md +0 -14
  226. data/examples/prompts/helper.md +0 -4
  227. data/examples/prompts/incident_responder.md +0 -18
  228. data/examples/prompts/keyword_extractor.md +0 -11
  229. data/examples/prompts/llm_config_demo.md +0 -20
  230. data/examples/prompts/order_support.md +0 -35
  231. data/examples/prompts/os_advocate.md +0 -13
  232. data/examples/prompts/os_chief.md +0 -13
  233. data/examples/prompts/os_editor.md +0 -13
  234. data/examples/prompts/parameterized_main_test.md +0 -6
  235. data/examples/prompts/pii_redactor.md +0 -7
  236. data/examples/prompts/product_support.md +0 -37
  237. data/examples/prompts/runbook_protocol.md +0 -12
  238. data/examples/prompts/sentiment_analyzer.md +0 -12
  239. data/examples/prompts/skill_a_test.md +0 -4
  240. data/examples/prompts/skill_b_test.md +0 -4
  241. data/examples/prompts/skill_config_test.md +0 -5
  242. data/examples/prompts/skill_cycle_a_test.md +0 -6
  243. data/examples/prompts/skill_cycle_b_test.md +0 -6
  244. data/examples/prompts/skill_description_test.md +0 -4
  245. data/examples/prompts/skill_leaf_test.md +0 -4
  246. data/examples/prompts/skill_nested_test.md +0 -6
  247. data/examples/prompts/skill_refs_main_test.md +0 -6
  248. data/examples/prompts/skill_self_ref_test.md +0 -6
  249. data/examples/prompts/skill_with_mcp_test.md +0 -9
  250. data/examples/prompts/skill_with_params_test.md +0 -6
  251. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  252. data/examples/prompts/skill_with_tools_test.md +0 -6
  253. data/examples/prompts/sre_compliance.md +0 -10
  254. data/examples/prompts/structured_output.md +0 -7
  255. data/examples/prompts/synthesizer.md +0 -17
  256. data/examples/prompts/technical.md +0 -10
  257. data/examples/prompts/template_with_skills_test.md +0 -6
  258. data/examples/prompts/triage.md +0 -22
  259. data/examples/ruboruby.md +0 -423
  260. data/examples/run_all.rb +0 -60
  261. data/examples/xyzzy.rb +0 -97
  262. data/lib/robot_lab/mcp/client.rb +0 -223
  263. data/lib/robot_lab/mcp/connection_poller.rb +0 -195
  264. data/lib/robot_lab/mcp/transports/base.rb +0 -64
  265. data/lib/robot_lab/mcp/transports/sse.rb +0 -120
  266. data/lib/robot_lab/mcp/transports/stdio.rb +0 -170
  267. data/lib/robot_lab/mcp/transports/streamable_http.rb +0 -141
  268. data/lib/robot_lab/mcp/transports/websocket.rb +0 -111
  269. data/logfile +0 -8
  270. data/mkdocs.yml +0 -209
  271. data/robot_concurrency.md +0 -38
  272. data/simple_acp_review.md +0 -298
  273. data/temp.md +0 -6
  274. data/tool_manifest_plan.md +0 -155
@@ -1,126 +0,0 @@
1
- # Messages
2
-
3
- Message types for LLM conversation representation.
4
-
5
- ## Overview
6
-
7
- RobotLab uses a structured message system to represent conversations between users, assistants, and tools.
8
-
9
- ```ruby
10
- # User input
11
- user_msg = UserMessage.new("Hello", session_id: "123")
12
-
13
- # Assistant response
14
- text_msg = TextMessage.new(role: "assistant", content: "Hi there!")
15
-
16
- # Tool interaction
17
- tool = ToolMessage.new(id: "call_1", name: "get_weather", input: { city: "NYC" })
18
- tool_call = ToolCallMessage.new(role: "assistant", tools: [tool])
19
- tool_result = ToolResultMessage.new(tool: tool, content: { data: { temp: 72 } })
20
- ```
21
-
22
- ## Message Hierarchy
23
-
24
- ```
25
- Message (base)
26
- ├── TextMessage - role + text content
27
- ├── ToolCallMessage - role + Array<ToolMessage>
28
- └── ToolResultMessage - tool + result content
29
-
30
- UserMessage - Standalone PORO (superclass Object)
31
- ToolMessage - Standalone PORO (superclass Object)
32
- ```
33
-
34
- `UserMessage` and `ToolMessage` are plain objects — their superclass is `Object`,
35
- not `Message`. They do not respond to `type`, `role`, or any of the predicates
36
- below.
37
-
38
- ## Common Interface
39
-
40
- All Message subclasses implement:
41
-
42
- ```ruby
43
- message.role # => String ("system", "user", "assistant", "tool_result")
44
- message.content # => String or structured data
45
- message.type # => String ("text", "tool_call", "tool_result")
46
- message.to_h # => Hash representation
47
- message.to_json # => JSON string
48
- ```
49
-
50
- Valid values are fixed by constants on `Message`, and the constructor raises
51
- `ArgumentError` for anything else:
52
-
53
- ```ruby
54
- RobotLab::Message::VALID_TYPES # => ["text", "tool_call", "tool_result"]
55
- RobotLab::Message::VALID_ROLES # => ["system", "user", "assistant", "tool_result"]
56
- RobotLab::Message::VALID_STOP_REASONS # => ["tool", "stop"]
57
- ```
58
-
59
- ### Nil keys and `to_h`
60
-
61
- `Message#to_h` — used as-is by `TextMessage` — ends in `.compact`, so any key
62
- whose value is `nil` is **absent** from the hash, not present with a `nil` value.
63
- A `TextMessage` built without a `stop_reason` produces a three-key hash. The same
64
- applies to `UserMessage#to_h`.
65
-
66
- `ToolCallMessage`, `ToolResultMessage`, and `ToolMessage` override `to_h` without
67
- compacting, so their keys are always present.
68
-
69
- Type and role predicates:
70
-
71
- ```ruby
72
- message.text? # => true if type is "text"
73
- message.tool_call? # => true if type is "tool_call"
74
- message.tool_result? # => true if type is "tool_result"
75
- message.system? # => true if role is "system"
76
- message.user? # => true if role is "user"
77
- message.assistant? # => true if role is "assistant"
78
- message.stopped? # => true if stop_reason is "stop"
79
- message.tool_stop? # => true if stop_reason is "tool"
80
- ```
81
-
82
- ## Classes
83
-
84
- | Class | Description |
85
- |-------|-------------|
86
- | [UserMessage](user-message.md) | User input with session and metadata |
87
- | [TextMessage](text-message.md) | Text message with role (system, user, or assistant) |
88
- | [ToolCallMessage](tool-call-message.md) | Tool invocation request containing ToolMessage objects |
89
- | [ToolResultMessage](tool-result-message.md) | Tool execution result |
90
-
91
- ## Usage in Memory
92
-
93
- Messages are typically accessed through memory:
94
-
95
- ```ruby
96
- memory.messages # => Array<Message>
97
-
98
- # Format for LLM
99
- memory.format_history # => Array<Message>
100
- ```
101
-
102
- ## Deserializing from Hash
103
-
104
- `Message.from_hash` reconstructs the correct subclass from a stored hash:
105
-
106
- ```ruby
107
- RobotLab::Message.from_hash({ type: "text", role: "user", content: "Hello" })
108
- # => #<RobotLab::TextMessage ...>
109
-
110
- RobotLab::Message.from_hash({ type: "tool_call", role: "assistant", tools: [...] })
111
- # => #<RobotLab::ToolCallMessage ...>
112
- ```
113
-
114
- When the `type` key is absent or `nil` (e.g. records persisted before the field was introduced), `from_hash` defaults to `TextMessage`:
115
-
116
- ```ruby
117
- RobotLab::Message.from_hash({ role: "user", content: "legacy row" })
118
- # => #<RobotLab::TextMessage ...>
119
- ```
120
-
121
- String keys are normalised automatically via `transform_keys(&:to_sym)`.
122
-
123
- ## See Also
124
-
125
- - [Memory](../core/memory.md)
126
- - [Message Flow Architecture](../../architecture/message-flow.md)
@@ -1,170 +0,0 @@
1
- # TextMessage
2
-
3
- Text message from system, user, or assistant.
4
-
5
- ## Class: `RobotLab::TextMessage`
6
-
7
- ```ruby
8
- message = TextMessage.new(role: "assistant", content: "Hello! How can I help you today?")
9
- ```
10
-
11
- ## Constructor
12
-
13
- ```ruby
14
- TextMessage.new(role:, content:, stop_reason: nil)
15
- ```
16
-
17
- **Parameters:**
18
-
19
- | Name | Type | Description |
20
- |------|------|-------------|
21
- | `role` | `String` | Message role ("system", "user", or "assistant") |
22
- | `content` | `String` | The text content |
23
- | `stop_reason` | `String`, `nil` | Stop reason ("stop" or "tool") |
24
-
25
- ## Attributes
26
-
27
- ### content
28
-
29
- ```ruby
30
- message.content # => String
31
- ```
32
-
33
- The text content.
34
-
35
- ### role
36
-
37
- ```ruby
38
- message.role # => "assistant"
39
- ```
40
-
41
- Returns a String: `"system"`, `"user"`, or `"assistant"`.
42
-
43
- ### type
44
-
45
- ```ruby
46
- message.type # => "text"
47
- ```
48
-
49
- Always returns `"text"`.
50
-
51
- ### stop_reason
52
-
53
- ```ruby
54
- message.stop_reason # => "stop" or nil
55
- ```
56
-
57
- The stop reason, if any.
58
-
59
- ## Methods
60
-
61
- ### to_h
62
-
63
- ```ruby
64
- message.to_h # => Hash
65
- ```
66
-
67
- Hash representation. Inherited from `Message#to_h`, which ends in `.compact` — a
68
- `nil` `stop_reason` is **omitted** rather than serialized as `nil`.
69
-
70
- **Returns:**
71
-
72
- ```ruby
73
- TextMessage.new(role: "assistant", content: "Hello! How can I help you today?").to_h
74
- # => { type: "text", role: "assistant", content: "Hello! How can I help you today?" }
75
-
76
- TextMessage.new(role: "assistant", content: "Done.", stop_reason: "stop").to_h
77
- # => { type: "text", role: "assistant", content: "Done.", stop_reason: "stop" }
78
- ```
79
-
80
- ### to_json
81
-
82
- ```ruby
83
- message.to_json # => String
84
- ```
85
-
86
- JSON representation.
87
-
88
- ### Predicates
89
-
90
- ```ruby
91
- message.text? # => true
92
- message.tool_call? # => false
93
- message.assistant? # => true (if role is "assistant")
94
- message.user? # => false
95
- message.stopped? # => true (if stop_reason is "stop")
96
- ```
97
-
98
- ## Examples
99
-
100
- ### System Message
101
-
102
- ```ruby
103
- message = TextMessage.new(role: "system", content: "You are a helpful assistant")
104
- message.system? # => true
105
- ```
106
-
107
- ### User Message
108
-
109
- ```ruby
110
- message = TextMessage.new(role: "user", content: "What's the weather?")
111
- message.user? # => true
112
- ```
113
-
114
- ### Assistant Response
115
-
116
- ```ruby
117
- message = TextMessage.new(
118
- role: "assistant",
119
- content: "Your order has shipped!",
120
- stop_reason: "stop"
121
- )
122
- message.assistant? # => true
123
- message.stopped? # => true
124
- ```
125
-
126
- ### In Robot Results
127
-
128
- `Robot#run` returns a `RobotResult`, **not** a `TextMessage`. `RobotResult` has no
129
- `text?` and no `content` — calling either raises `NoMethodError`. Read the reply
130
- with `last_text_content` (aliased as `reply`):
131
-
132
- ```ruby
133
- result = robot.run("Tell me a joke")
134
-
135
- puts result.last_text_content # => the assistant's text
136
- puts result.reply # => same thing
137
- result.stopped? # => true whenever there are no tool calls
138
- result.has_tool_calls? # => whether the final message carried tool calls
139
- ```
140
-
141
- !!! note "`RobotResult#stopped?` is not driven by `stop_reason`"
142
- A `RobotResult` produced by `Robot#run` always has `stop_reason == nil`
143
- (`RubyLLM::Message` does not define the method, so `build_result` falls back
144
- to `nil`), and the `TextMessage` it wraps is built without a `stop_reason`
145
- too. `stopped?` therefore reduces to `!has_tool_calls?` — it never becomes
146
- true because a `stop_reason` of `"stop"` was reported. The
147
- `stop_reason: "stop"` form shown above only applies to `TextMessage`
148
- instances you construct yourself.
149
-
150
- `result.output` is an `Array` holding a single `TextMessage` rebuilt from the
151
- final response text — it is not the full turn:
152
-
153
- ```ruby
154
- msg = result.output.first
155
- msg.text? # => true
156
- msg.content # => the assistant's text
157
- ```
158
-
159
- ### Filtering Text Content
160
-
161
- ```ruby
162
- # Get only text messages from memory
163
- text_messages = memory.messages.select(&:text?).map(&:content)
164
- ```
165
-
166
- ## See Also
167
-
168
- - [UserMessage](user-message.md)
169
- - [ToolCallMessage](tool-call-message.md)
170
- - [Robot](../core/robot.md)
@@ -1,206 +0,0 @@
1
- # ToolCallMessage
2
-
3
- Tool invocation request from the LLM.
4
-
5
- ## Class: `RobotLab::ToolCallMessage`
6
-
7
- ```ruby
8
- message = ToolCallMessage.new(
9
- role: "assistant",
10
- tools: [
11
- ToolMessage.new(id: "call_abc123", name: "get_weather", input: { city: "New York" })
12
- ]
13
- )
14
- ```
15
-
16
- ## Constructor
17
-
18
- ```ruby
19
- ToolCallMessage.new(role:, tools:, stop_reason: nil)
20
- ```
21
-
22
- **Parameters:**
23
-
24
- | Name | Type | Description |
25
- |------|------|-------------|
26
- | `role` | `String` | Message role (typically "assistant") |
27
- | `tools` | `Array<ToolMessage, Hash>` | Tool calls; plain Hashes are normalized via `ToolMessage.from_hash` |
28
- | `stop_reason` | `String`, `nil` | Stop reason (defaults to "tool") |
29
-
30
- Each entry in `tools` may be a `ToolMessage` **or** a Hash. Anything else raises
31
- `ArgumentError: Invalid tool: must be ToolMessage or Hash`. The `tools` reader
32
- always returns `ToolMessage` objects.
33
-
34
- ```ruby
35
- ToolCallMessage.new(
36
- role: "assistant",
37
- tools: [{ id: "call_1", name: "get_weather", arguments: { city: "NYC" } }]
38
- ).tools.first.input
39
- # => { city: "NYC" }
40
- ```
41
-
42
- `ToolMessage.from_hash` symbolizes keys and accepts either `input:` or
43
- `arguments:` for the parameter hash (`input` wins), defaulting to `{}` when
44
- neither is present. This makes raw provider tool-call payloads usable directly.
45
-
46
- ## ToolMessage
47
-
48
- Each tool call is represented by a standalone `ToolMessage` object. It is a plain
49
- Ruby object — its superclass is `Object`, **not** `Message` — so it has no
50
- `type`, `role`, or predicate methods:
51
-
52
- ```ruby
53
- ToolMessage.new(id:, name:, input:)
54
- ToolMessage.from_hash(hash)
55
- ```
56
-
57
- | Name | Type | Description |
58
- |------|------|-------------|
59
- | `id` | `String` | Unique call identifier |
60
- | `name` | `String` | Tool name |
61
- | `input` | `Hash`, `nil` | Tool parameters; `nil` becomes `{}` |
62
-
63
- `ToolMessage#to_h` returns `{ type: "tool", id:, name:, input: }`. It is not
64
- compacted, so every key is always present.
65
-
66
- ## Attributes
67
-
68
- ### tools
69
-
70
- ```ruby
71
- message.tools # => Array<ToolMessage>
72
- ```
73
-
74
- Array of `ToolMessage` objects representing the tool calls.
75
-
76
- ### role
77
-
78
- ```ruby
79
- message.role # => "assistant"
80
- ```
81
-
82
- Returns a String. The LLM initiates tool calls, so this is typically `"assistant"`.
83
-
84
- ### type
85
-
86
- ```ruby
87
- message.type # => "tool_call"
88
- ```
89
-
90
- Always returns `"tool_call"`.
91
-
92
- ### content
93
-
94
- ```ruby
95
- message.content # => nil
96
- ```
97
-
98
- Always `nil` for tool call messages (the tool data is in `tools`).
99
-
100
- ### stop_reason
101
-
102
- ```ruby
103
- message.stop_reason # => "tool"
104
- ```
105
-
106
- Defaults to `"tool"` indicating the conversation stopped for tool execution.
107
-
108
- ## Methods
109
-
110
- ### to_h
111
-
112
- ```ruby
113
- message.to_h # => Hash
114
- ```
115
-
116
- Hash representation. `ToolCallMessage` overrides `Message#to_h` and does **not**
117
- compact, so all four keys are always present (`content` is not included at all).
118
-
119
- **Returns:**
120
-
121
- ```ruby
122
- {
123
- type: "tool_call",
124
- role: "assistant",
125
- tools: [
126
- { type: "tool", id: "call_abc123", name: "get_weather", input: { city: "New York" } }
127
- ],
128
- stop_reason: "tool"
129
- }
130
- ```
131
-
132
- ### to_json
133
-
134
- ```ruby
135
- message.to_json # => String
136
- ```
137
-
138
- JSON representation.
139
-
140
- ### Predicates
141
-
142
- ```ruby
143
- message.tool_call? # => true
144
- message.text? # => false
145
- message.assistant? # => true
146
- message.tool_stop? # => true
147
- ```
148
-
149
- ## Examples
150
-
151
- ### Single Tool Call
152
-
153
- ```ruby
154
- tool = ToolMessage.new(
155
- id: "call_1",
156
- name: "search_orders",
157
- input: { user_id: "123", status: "pending" }
158
- )
159
-
160
- call = ToolCallMessage.new(role: "assistant", tools: [tool])
161
- ```
162
-
163
- ### Multiple Tool Calls
164
-
165
- ```ruby
166
- tools = [
167
- ToolMessage.new(id: "call_1", name: "get_weather", input: { city: "NYC" }),
168
- ToolMessage.new(id: "call_2", name: "get_time", input: { timezone: "EST" })
169
- ]
170
-
171
- call = ToolCallMessage.new(role: "assistant", tools: tools)
172
- call.tools.length # => 2
173
- ```
174
-
175
- ### Processing Tool Calls
176
-
177
- ```ruby
178
- if message.tool_call?
179
- message.tools.each do |tool|
180
- puts "Tool called: #{tool.name}"
181
- puts "Parameters: #{tool.input.inspect}"
182
- end
183
- end
184
- ```
185
-
186
- ### In Tool Execution Flow
187
-
188
- ```ruby
189
- # LLM returns a tool call
190
- tool = ToolMessage.new(id: "call_weather_1", name: "get_weather", input: { city: "Seattle" })
191
- tool_call = ToolCallMessage.new(role: "assistant", tools: [tool])
192
-
193
- # Execute the tool and record the result
194
- result_data = execute_tool(tool.name, tool.input)
195
-
196
- tool_result = ToolResultMessage.new(
197
- tool: tool,
198
- content: { data: result_data }
199
- )
200
- ```
201
-
202
- ## See Also
203
-
204
- - [ToolResultMessage](tool-result-message.md)
205
- - [Tool](../core/tool.md)
206
- - [Using Tools Guide](../../guides/using-tools.md)