robot_lab 0.1.0 → 0.2.1

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/.architecture/AGENTS.md +32 -0
  3. data/.architecture/config.yml +8 -0
  4. data/.architecture/members.yml +60 -0
  5. data/.architecture/reviews/feature-free-will.md +490 -0
  6. data/.architecture/reviews/overall-codebase.md +427 -0
  7. data/.claude/settings.local.json +57 -0
  8. data/.codex/config.toml +2 -0
  9. data/.irbrc +2 -2
  10. data/.rubocop.yml +172 -0
  11. data/CHANGELOG.md +72 -0
  12. data/CLAUDE.md +139 -0
  13. data/README.md +91 -95
  14. data/Rakefile +109 -3
  15. data/agent2agent_review.md +192 -0
  16. data/agentf_improvements.md +253 -0
  17. data/agents.md +14 -0
  18. data/docs/examples/index.md +37 -2
  19. data/docs/getting-started/configuration.md +20 -7
  20. data/docs/guides/index.md +16 -16
  21. data/docs/guides/knowledge.md +7 -1
  22. data/docs/guides/observability.md +132 -0
  23. data/docs/index.md +30 -3
  24. data/docs/superpowers/plans/2026-05-06-agentskills.md +1303 -0
  25. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +247 -0
  26. data/examples/.envrc +1 -0
  27. data/examples/01_simple_robot.rb +5 -9
  28. data/examples/02_tools.rb +5 -9
  29. data/examples/03_network.rb +8 -9
  30. data/examples/04_mcp.rb +21 -29
  31. data/examples/05_streaming.rb +12 -18
  32. data/examples/06_prompt_templates.rb +11 -19
  33. data/examples/07_network_memory.rb +16 -31
  34. data/examples/08_llm_config.rb +10 -22
  35. data/examples/09_chaining.rb +16 -27
  36. data/examples/10_memory.rb +12 -28
  37. data/examples/11_network_introspection.rb +15 -29
  38. data/examples/12_message_bus.rb +5 -12
  39. data/examples/13_spawn.rb +5 -10
  40. data/examples/14_rusty_circuit/.envrc +1 -0
  41. data/examples/14_rusty_circuit/comic.rb +2 -0
  42. data/examples/14_rusty_circuit/heckler.rb +1 -1
  43. data/examples/14_rusty_circuit/open_mic.rb +1 -3
  44. data/examples/14_rusty_circuit/scout.rb +2 -0
  45. data/examples/15_memory_network_and_bus/.envrc +1 -0
  46. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +6 -3
  47. data/examples/15_memory_network_and_bus/linux_writer.rb +1 -1
  48. data/examples/15_memory_network_and_bus/output/combined_article.md +6 -6
  49. data/examples/15_memory_network_and_bus/output/final_article.md +6 -8
  50. data/examples/15_memory_network_and_bus/output/linux_draft.md +4 -2
  51. data/examples/15_memory_network_and_bus/output/mac_draft.md +3 -3
  52. data/examples/15_memory_network_and_bus/output/memory.json +6 -6
  53. data/examples/15_memory_network_and_bus/output/revision_1.md +10 -11
  54. data/examples/15_memory_network_and_bus/output/revision_2.md +6 -8
  55. data/examples/15_memory_network_and_bus/output/windows_draft.md +3 -3
  56. data/examples/16_writers_room/.envrc +1 -0
  57. data/examples/16_writers_room/writers_room.rb +2 -4
  58. data/examples/17_skills.rb +8 -17
  59. data/examples/18_rails/Gemfile +1 -0
  60. data/examples/19_token_tracking.rb +9 -15
  61. data/examples/20_circuit_breaker.rb +10 -19
  62. data/examples/21_learning_loop.rb +11 -20
  63. data/examples/22_context_compression.rb +6 -13
  64. data/examples/23_convergence.rb +6 -17
  65. data/examples/24_structured_delegation.rb +11 -15
  66. data/examples/25_history_search.rb +5 -12
  67. data/examples/26_document_store.rb +6 -13
  68. data/examples/27_incident_response/incident_response.rb +4 -5
  69. data/examples/28_mcp_discovery.rb +8 -11
  70. data/examples/29_ractor_tools.rb +4 -9
  71. data/examples/30_ractor_network.rb +10 -19
  72. data/examples/31_launch_assessment.rb +10 -23
  73. data/examples/32_newsletter_reader.rb +188 -0
  74. data/examples/33_stock_generator.rb +80 -0
  75. data/examples/33_stock_predictor.rb +306 -0
  76. data/examples/34_agentskills.rb +72 -0
  77. data/examples/README.md +1 -1
  78. data/examples/common.rb +76 -0
  79. data/examples/ruboruby.md +423 -0
  80. data/examples/temp.md +51 -0
  81. data/lib/robot_lab/agent_skill.rb +63 -0
  82. data/lib/robot_lab/agent_skill_catalog.rb +74 -0
  83. data/lib/robot_lab/ask_user.rb +2 -2
  84. data/lib/robot_lab/bus_poller.rb +12 -5
  85. data/lib/robot_lab/config.rb +1 -12
  86. data/lib/robot_lab/delegation_future.rb +1 -1
  87. data/lib/robot_lab/doom_loop_detector.rb +98 -0
  88. data/lib/robot_lab/history_compressor.rb +4 -10
  89. data/lib/robot_lab/mcp/client.rb +1 -2
  90. data/lib/robot_lab/mcp/connection_poller.rb +3 -3
  91. data/lib/robot_lab/mcp/server.rb +1 -1
  92. data/lib/robot_lab/mcp/server_discovery.rb +0 -2
  93. data/lib/robot_lab/memory.rb +32 -27
  94. data/lib/robot_lab/memory_change.rb +2 -2
  95. data/lib/robot_lab/message.rb +4 -4
  96. data/lib/robot_lab/network.rb +11 -6
  97. data/lib/robot_lab/robot/agent_skill_matching.rb +99 -0
  98. data/lib/robot_lab/robot/bus_messaging.rb +9 -27
  99. data/lib/robot_lab/robot/history_search.rb +4 -1
  100. data/lib/robot_lab/robot/mcp_management.rb +5 -11
  101. data/lib/robot_lab/robot/template_rendering.rb +60 -40
  102. data/lib/robot_lab/robot.rb +323 -206
  103. data/lib/robot_lab/robot_result.rb +6 -5
  104. data/lib/robot_lab/run_config.rb +5 -11
  105. data/lib/robot_lab/script_tool.rb +76 -0
  106. data/lib/robot_lab/state_proxy.rb +7 -5
  107. data/lib/robot_lab/tool.rb +3 -3
  108. data/lib/robot_lab/tool_config.rb +1 -1
  109. data/lib/robot_lab/tool_manifest.rb +5 -7
  110. data/lib/robot_lab/user_message.rb +2 -2
  111. data/lib/robot_lab/version.rb +1 -1
  112. data/lib/robot_lab/waiter.rb +1 -1
  113. data/lib/robot_lab.rb +41 -52
  114. data/logfile +8 -0
  115. data/mkdocs.yml +2 -3
  116. data/robot_concurrency.md +38 -0
  117. data/simple_acp_review.md +298 -0
  118. data/site/404.html +2300 -0
  119. data/site/api/core/index.html +2706 -0
  120. data/site/api/core/memory/index.html +3793 -0
  121. data/site/api/core/network/index.html +3500 -0
  122. data/site/api/core/robot/index.html +4566 -0
  123. data/site/api/core/state/index.html +3390 -0
  124. data/site/api/core/tool/index.html +3843 -0
  125. data/site/api/index.html +2635 -0
  126. data/site/api/mcp/client/index.html +3435 -0
  127. data/site/api/mcp/index.html +2783 -0
  128. data/site/api/mcp/server/index.html +3252 -0
  129. data/site/api/mcp/transports/index.html +3352 -0
  130. data/site/api/messages/index.html +2641 -0
  131. data/site/api/messages/text-message/index.html +3087 -0
  132. data/site/api/messages/tool-call-message/index.html +3159 -0
  133. data/site/api/messages/tool-result-message/index.html +3252 -0
  134. data/site/api/messages/user-message/index.html +3212 -0
  135. data/site/api/streaming/context/index.html +3282 -0
  136. data/site/api/streaming/events/index.html +3347 -0
  137. data/site/api/streaming/index.html +2738 -0
  138. data/site/architecture/core-concepts/index.html +3757 -0
  139. data/site/architecture/index.html +2797 -0
  140. data/site/architecture/message-flow/index.html +3238 -0
  141. data/site/architecture/network-orchestration/index.html +3433 -0
  142. data/site/architecture/robot-execution/index.html +3140 -0
  143. data/site/architecture/state-management/index.html +3498 -0
  144. data/site/assets/css/custom.css +56 -0
  145. data/site/assets/images/favicon.png +0 -0
  146. data/site/assets/images/robot_lab.jpg +0 -0
  147. data/site/assets/javascripts/bundle.79ae519e.min.js +16 -0
  148. data/site/assets/javascripts/bundle.79ae519e.min.js.map +7 -0
  149. data/site/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
  150. data/site/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
  151. data/site/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
  152. data/site/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
  153. data/site/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
  154. data/site/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
  155. data/site/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
  156. data/site/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
  157. data/site/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
  158. data/site/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
  159. data/site/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
  160. data/site/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
  161. data/site/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
  162. data/site/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
  163. data/site/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
  164. data/site/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
  165. data/site/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
  166. data/site/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
  167. data/site/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
  168. data/site/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
  169. data/site/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
  170. data/site/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
  171. data/site/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
  172. data/site/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
  173. data/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
  174. data/site/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
  175. data/site/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
  176. data/site/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
  177. data/site/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
  178. data/site/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
  179. data/site/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
  180. data/site/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
  181. data/site/assets/javascripts/lunr/tinyseg.js +206 -0
  182. data/site/assets/javascripts/lunr/wordcut.js +6708 -0
  183. data/site/assets/javascripts/workers/search.2c215733.min.js +42 -0
  184. data/site/assets/javascripts/workers/search.2c215733.min.js.map +7 -0
  185. data/site/assets/stylesheets/main.484c7ddc.min.css +1 -0
  186. data/site/assets/stylesheets/main.484c7ddc.min.css.map +1 -0
  187. data/site/assets/stylesheets/palette.ab4e12ef.min.css +1 -0
  188. data/site/assets/stylesheets/palette.ab4e12ef.min.css.map +1 -0
  189. data/site/concepts/index.html +3455 -0
  190. data/site/examples/basic-chat/index.html +2880 -0
  191. data/site/examples/index.html +2907 -0
  192. data/site/examples/mcp-server/index.html +3018 -0
  193. data/site/examples/multi-robot-network/index.html +3131 -0
  194. data/site/examples/rails-application/index.html +3329 -0
  195. data/site/examples/tool-usage/index.html +3085 -0
  196. data/site/getting-started/configuration/index.html +3745 -0
  197. data/site/getting-started/index.html +2572 -0
  198. data/site/getting-started/installation/index.html +2981 -0
  199. data/site/getting-started/quick-start/index.html +2942 -0
  200. data/site/guides/building-robots/index.html +4290 -0
  201. data/site/guides/creating-networks/index.html +3858 -0
  202. data/site/guides/index.html +2586 -0
  203. data/site/guides/mcp-integration/index.html +3581 -0
  204. data/site/guides/memory/index.html +3586 -0
  205. data/site/guides/rails-integration/index.html +4019 -0
  206. data/site/guides/streaming/index.html +3157 -0
  207. data/site/guides/using-tools/index.html +3802 -0
  208. data/site/index.html +2671 -0
  209. data/site/search/search_index.json +1 -0
  210. data/site/sitemap.xml +183 -0
  211. data/site/sitemap.xml.gz +0 -0
  212. data/site/tags.json +1 -0
  213. data/temp.md +6 -0
  214. data/tool_manifest_plan.md +155 -0
  215. metadata +154 -92
  216. data/docs/examples/rails-application.md +0 -419
  217. data/docs/guides/ractor-parallelism.md +0 -364
  218. data/docs/guides/rails-integration.md +0 -681
  219. data/docs/superpowers/plans/2026-04-14-ractor-integration.md +0 -1538
  220. data/docs/superpowers/specs/2026-04-14-ractor-integration-design.md +0 -258
  221. data/lib/generators/robot_lab/install_generator.rb +0 -90
  222. data/lib/generators/robot_lab/job_generator.rb +0 -40
  223. data/lib/generators/robot_lab/robot_generator.rb +0 -55
  224. data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -42
  225. data/lib/generators/robot_lab/templates/job.rb.tt +0 -21
  226. data/lib/generators/robot_lab/templates/migration.rb.tt +0 -32
  227. data/lib/generators/robot_lab/templates/result_model.rb.tt +0 -52
  228. data/lib/generators/robot_lab/templates/robot.rb.tt +0 -31
  229. data/lib/generators/robot_lab/templates/robot_job.rb.tt +0 -18
  230. data/lib/generators/robot_lab/templates/robot_test.rb.tt +0 -34
  231. data/lib/generators/robot_lab/templates/routing_robot.rb.tt +0 -59
  232. data/lib/generators/robot_lab/templates/thread_model.rb.tt +0 -40
  233. data/lib/robot_lab/document_store.rb +0 -155
  234. data/lib/robot_lab/ractor_boundary.rb +0 -42
  235. data/lib/robot_lab/ractor_job.rb +0 -37
  236. data/lib/robot_lab/ractor_memory_proxy.rb +0 -85
  237. data/lib/robot_lab/ractor_network_scheduler.rb +0 -154
  238. data/lib/robot_lab/ractor_worker_pool.rb +0 -117
  239. data/lib/robot_lab/rails_integration/engine.rb +0 -29
  240. data/lib/robot_lab/rails_integration/job.rb +0 -158
  241. data/lib/robot_lab/rails_integration/railtie.rb +0 -51
  242. data/lib/robot_lab/rails_integration/turbo_stream_callbacks.rb +0 -72
@@ -1,419 +0,0 @@
1
- # Rails Application
2
-
3
- Full Rails integration with Action Cable and background jobs.
4
-
5
- ## Overview
6
-
7
- This example demonstrates integrating RobotLab into a Rails application with real-time streaming via Action Cable, background job processing, and persistent conversation history.
8
-
9
- ## Setup
10
-
11
- ### 1. Add to Gemfile
12
-
13
- ```ruby
14
- # Gemfile
15
- gem "robot_lab"
16
- ```
17
-
18
- ### 2. Run Generator
19
-
20
- ```bash
21
- rails generate robot_lab:install
22
- ```
23
-
24
- This creates:
25
-
26
- - `config/initializers/robot_lab.rb`
27
- - `app/robots/` directory
28
- - `app/tools/` directory
29
- - Database migrations for conversation history
30
-
31
- ### 3. Run Migrations
32
-
33
- ```bash
34
- rails db:migrate
35
- ```
36
-
37
- ## Configuration
38
-
39
- RobotLab uses MywayConfig for configuration. There is no `RobotLab.configure` block. Instead, configuration is loaded automatically from multiple sources in priority order:
40
-
41
- 1. Bundled defaults (`lib/robot_lab/config/defaults.yml`)
42
- 2. Environment-specific overrides (development, test, production)
43
- 3. XDG user config (`~/.config/robot_lab/config.yml`)
44
- 4. Project config (`./config/robot_lab.yml`)
45
- 5. Environment variables (`ROBOT_LAB_*` prefix)
46
-
47
- ### Config File
48
-
49
- ```yaml
50
- # config/robot_lab.yml
51
- defaults:
52
- ruby_llm:
53
- model: claude-sonnet-4
54
- anthropic_api_key: <%= ENV['ANTHROPIC_API_KEY'] %>
55
-
56
- development:
57
- ruby_llm:
58
- log_level: :debug
59
-
60
- production:
61
- ruby_llm:
62
- request_timeout: 180
63
- max_retries: 5
64
- ```
65
-
66
- ### Environment Variables
67
-
68
- ```bash
69
- # Provider API keys
70
- ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY=sk-ant-...
71
- ROBOT_LAB_RUBY_LLM__OPENAI_API_KEY=sk-...
72
-
73
- # Model configuration
74
- ROBOT_LAB_RUBY_LLM__MODEL=claude-sonnet-4
75
- ROBOT_LAB_RUBY_LLM__REQUEST_TIMEOUT=120
76
- ```
77
-
78
- ### Accessing Configuration
79
-
80
- ```ruby
81
- # Read configuration values at runtime
82
- RobotLab.config.ruby_llm.model #=> "claude-sonnet-4"
83
- RobotLab.config.ruby_llm.request_timeout #=> 120
84
-
85
- # The logger defaults to Rails.logger when running in Rails
86
- RobotLab.config.logger #=> Rails.logger
87
- ```
88
-
89
- ### Rails Engine and Railtie
90
-
91
- RobotLab provides both a Rails Engine (`RobotLab::RailsIntegration::Engine`) and a Railtie (`RobotLab::RailsIntegration::Railtie`). These are loaded automatically when Rails is detected. The Engine isolates the RobotLab namespace and adds `app/robots` and `app/tools` to the autoload paths. The Railtie loads rake tasks and generators.
92
-
93
- ## Models
94
-
95
- ```ruby
96
- # app/models/conversation_thread.rb
97
- class ConversationThread < ApplicationRecord
98
- belongs_to :user
99
- has_many :messages, class_name: "ConversationMessage", dependent: :destroy
100
-
101
- validates :external_id, presence: true, uniqueness: true
102
-
103
- def self.find_or_create_for(user:, external_id: nil)
104
- external_id ||= SecureRandom.uuid
105
- find_or_create_by!(user: user, external_id: external_id)
106
- end
107
- end
108
-
109
- # app/models/conversation_message.rb
110
- class ConversationMessage < ApplicationRecord
111
- belongs_to :thread, class_name: "ConversationThread"
112
-
113
- validates :role, presence: true
114
- validates :content, presence: true
115
-
116
- scope :ordered, -> { order(:position) }
117
- end
118
- ```
119
-
120
- ## Robot Definitions
121
-
122
- Robots are built using `RobotLab.build` with named parameters. Tools are Ruby classes that inherit from `RubyLLM::Tool`.
123
-
124
- ```ruby
125
- # app/tools/get_user_info_tool.rb
126
- class GetUserInfoTool < RubyLLM::Tool
127
- description "Get information about the current user"
128
-
129
- param :user_id, type: :integer, desc: "The user ID to look up"
130
-
131
- def execute(user_id:)
132
- user = User.find(user_id)
133
- {
134
- name: user.name,
135
- email: user.email,
136
- plan: user.subscription&.plan || "free",
137
- member_since: user.created_at.to_date.to_s
138
- }
139
- rescue ActiveRecord::RecordNotFound
140
- { error: "User not found" }
141
- end
142
- end
143
-
144
- # app/tools/get_orders_tool.rb
145
- class GetOrdersTool < RubyLLM::Tool
146
- description "Get user's recent orders"
147
-
148
- param :user_id, type: :integer, desc: "The user ID"
149
- param :limit, type: :integer, desc: "Number of orders to return", default: 5
150
-
151
- def execute(user_id:, limit: 5)
152
- orders = Order.where(user_id: user_id)
153
- .order(created_at: :desc)
154
- .limit(limit)
155
-
156
- orders.map do |order|
157
- {
158
- id: order.external_id,
159
- status: order.status,
160
- total: order.total.to_f,
161
- created_at: order.created_at.iso8601
162
- }
163
- end
164
- end
165
- end
166
-
167
- # app/tools/create_ticket_tool.rb
168
- class CreateTicketTool < RubyLLM::Tool
169
- description "Create a support ticket"
170
-
171
- param :user_id, type: :integer, desc: "The user ID"
172
- param :subject, type: :string, desc: "Ticket subject"
173
- param :description, type: :string, desc: "Ticket description"
174
- param :priority, type: :string, desc: "Priority level", enum: %w[low medium high]
175
-
176
- def execute(user_id:, subject:, description:, priority: "medium")
177
- ticket = SupportTicket.create!(
178
- user_id: user_id,
179
- subject: subject,
180
- description: description,
181
- priority: priority
182
- )
183
-
184
- {
185
- success: true,
186
- ticket_id: ticket.external_id,
187
- message: "Ticket created successfully"
188
- }
189
- rescue => e
190
- { success: false, error: e.message }
191
- end
192
- end
193
- ```
194
-
195
- ```ruby
196
- # app/robots/support_robot.rb
197
- class SupportRobot
198
- def self.build(user_id:)
199
- RobotLab.build(
200
- name: "support",
201
- system_prompt: <<~PROMPT,
202
- You are a helpful customer support assistant for our company.
203
- Be friendly, professional, and thorough in your responses.
204
- If you need to look up information, use the available tools.
205
- The current user ID is #{user_id}.
206
- PROMPT
207
- local_tools: [GetUserInfoTool, GetOrdersTool, CreateTicketTool]
208
- )
209
- end
210
- end
211
- ```
212
-
213
- ## Network Configuration
214
-
215
- Networks use `create_network` with a block DSL that defines tasks and their dependencies:
216
-
217
- ```ruby
218
- # app/robots/support_network.rb
219
- class SupportNetwork
220
- def self.build(user_id:)
221
- support = SupportRobot.build(user_id: user_id)
222
-
223
- RobotLab.create_network(name: "support_network") do
224
- task :support, support, depends_on: :none
225
- end
226
- end
227
- end
228
- ```
229
-
230
- ## Service Object
231
-
232
- ```ruby
233
- # app/services/chat_service.rb
234
- class ChatService
235
- def initialize(user:, thread_id: nil)
236
- @user = user
237
- @thread_id = thread_id
238
- end
239
-
240
- def call(message:)
241
- robot = SupportRobot.build(user_id: @user.id)
242
- result = robot.run(message)
243
-
244
- {
245
- response: result.last_text_content,
246
- has_tool_calls: result.has_tool_calls?
247
- }
248
- end
249
- end
250
- ```
251
-
252
- ## Controller
253
-
254
- ```ruby
255
- # app/controllers/api/chats_controller.rb
256
- module Api
257
- class ChatsController < ApplicationController
258
- before_action :authenticate_user!
259
-
260
- def create
261
- service = ChatService.new(
262
- user: current_user,
263
- thread_id: params[:thread_id]
264
- )
265
-
266
- result = service.call(message: params[:message])
267
-
268
- render json: {
269
- response: result[:response]
270
- }
271
- end
272
- end
273
- end
274
- ```
275
-
276
- ## Action Cable Integration
277
-
278
- ```ruby
279
- # app/channels/chat_channel.rb
280
- class ChatChannel < ApplicationCable::Channel
281
- def subscribed
282
- stream_for current_user
283
- end
284
-
285
- def receive(data)
286
- ChatJob.perform_later(
287
- user_id: current_user.id,
288
- thread_id: data["thread_id"],
289
- message: data["message"]
290
- )
291
- end
292
- end
293
-
294
- # app/jobs/chat_job.rb
295
- class ChatJob < ApplicationJob
296
- queue_as :default
297
-
298
- def perform(user_id:, thread_id:, message:)
299
- user = User.find(user_id)
300
- robot = SupportRobot.build(user_id: user.id)
301
-
302
- result = robot.run(message)
303
-
304
- ChatChannel.broadcast_to(
305
- user,
306
- type: "complete",
307
- content: result.last_text_content
308
- )
309
- end
310
- end
311
- ```
312
-
313
- ## Frontend (Stimulus)
314
-
315
- ```javascript
316
- // app/javascript/controllers/chat_controller.js
317
- import { Controller } from "@hotwired/stimulus"
318
- import { createConsumer } from "@rails/actioncable"
319
-
320
- export default class extends Controller {
321
- static targets = ["messages", "input", "response"]
322
-
323
- connect() {
324
- this.consumer = createConsumer()
325
- this.channel = this.consumer.subscriptions.create("ChatChannel", {
326
- received: (data) => this.handleMessage(data)
327
- })
328
- }
329
-
330
- disconnect() {
331
- this.channel?.unsubscribe()
332
- }
333
-
334
- send() {
335
- const message = this.inputTarget.value.trim()
336
- if (!message) return
337
-
338
- this.appendMessage("user", message)
339
- this.inputTarget.value = ""
340
-
341
- // Create response container
342
- this.currentResponse = document.createElement("div")
343
- this.currentResponse.className = "message assistant"
344
- this.messagesTarget.appendChild(this.currentResponse)
345
-
346
- this.channel.send({
347
- message: message,
348
- thread_id: this.threadId
349
- })
350
- }
351
-
352
- handleMessage(data) {
353
- switch (data.type) {
354
- case "complete":
355
- this.currentResponse.textContent = data.content
356
- break
357
- }
358
- }
359
-
360
- appendMessage(role, content) {
361
- const div = document.createElement("div")
362
- div.className = `message ${role}`
363
- div.textContent = content
364
- this.messagesTarget.appendChild(div)
365
- }
366
- }
367
- ```
368
-
369
- ## View
370
-
371
- ```erb
372
- <!-- app/views/chats/show.html.erb -->
373
- <div data-controller="chat">
374
- <div class="messages" data-chat-target="messages">
375
- <!-- Messages appear here -->
376
- </div>
377
-
378
- <form data-action="submit->chat#send">
379
- <input type="text"
380
- data-chat-target="input"
381
- placeholder="Type a message..."
382
- autocomplete="off">
383
- <button type="submit">Send</button>
384
- </form>
385
- </div>
386
- ```
387
-
388
- ## Running
389
-
390
- ```bash
391
- # Install dependencies
392
- bundle install
393
- yarn install
394
-
395
- # Setup database
396
- rails db:migrate
397
-
398
- # Set API key (or configure via config/robot_lab.yml)
399
- export ANTHROPIC_API_KEY="your-key"
400
-
401
- # Start server
402
- bin/dev
403
- ```
404
-
405
- ## Key Concepts
406
-
407
- 1. **Robot Factory**: `RobotLab.build(name:, system_prompt:, local_tools:, ...)` creates robot instances
408
- 2. **MywayConfig**: Configuration via YAML files and environment variables, not a configure block
409
- 3. **`robot.run("message")`**: Send a message as a positional string argument
410
- 4. **`result.last_text_content`**: Extract the response text from a `RobotResult`
411
- 5. **Memory**: Robots have `robot.memory` for key-value storage; networks share memory
412
- 6. **Tools**: Ruby classes inheriting from `RubyLLM::Tool`, passed via `local_tools:`
413
- 7. **Action Cable**: Real-time streaming to browser
414
- 8. **Background Jobs**: Non-blocking processing
415
-
416
- ## See Also
417
-
418
- - [Rails Integration Guide](../guides/rails-integration.md)
419
- - [Streaming Guide](../guides/streaming.md)