robot_lab 0.0.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 (153) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +1 -0
  3. data/.github/workflows/deploy-github-pages.yml +52 -0
  4. data/.github/workflows/deploy-yard-docs.yml +52 -0
  5. data/CHANGELOG.md +55 -0
  6. data/COMMITS.md +196 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +332 -0
  9. data/Rakefile +67 -0
  10. data/docs/api/adapters/anthropic.md +121 -0
  11. data/docs/api/adapters/gemini.md +133 -0
  12. data/docs/api/adapters/index.md +104 -0
  13. data/docs/api/adapters/openai.md +134 -0
  14. data/docs/api/core/index.md +113 -0
  15. data/docs/api/core/memory.md +314 -0
  16. data/docs/api/core/network.md +291 -0
  17. data/docs/api/core/robot.md +273 -0
  18. data/docs/api/core/state.md +273 -0
  19. data/docs/api/core/tool.md +353 -0
  20. data/docs/api/history/active-record-adapter.md +195 -0
  21. data/docs/api/history/config.md +191 -0
  22. data/docs/api/history/index.md +132 -0
  23. data/docs/api/history/thread-manager.md +144 -0
  24. data/docs/api/index.md +82 -0
  25. data/docs/api/mcp/client.md +221 -0
  26. data/docs/api/mcp/index.md +111 -0
  27. data/docs/api/mcp/server.md +225 -0
  28. data/docs/api/mcp/transports.md +264 -0
  29. data/docs/api/messages/index.md +67 -0
  30. data/docs/api/messages/text-message.md +102 -0
  31. data/docs/api/messages/tool-call-message.md +144 -0
  32. data/docs/api/messages/tool-result-message.md +154 -0
  33. data/docs/api/messages/user-message.md +171 -0
  34. data/docs/api/streaming/context.md +174 -0
  35. data/docs/api/streaming/events.md +237 -0
  36. data/docs/api/streaming/index.md +108 -0
  37. data/docs/architecture/core-concepts.md +243 -0
  38. data/docs/architecture/index.md +138 -0
  39. data/docs/architecture/message-flow.md +320 -0
  40. data/docs/architecture/network-orchestration.md +216 -0
  41. data/docs/architecture/robot-execution.md +243 -0
  42. data/docs/architecture/state-management.md +323 -0
  43. data/docs/assets/css/custom.css +56 -0
  44. data/docs/assets/images/robot_lab.jpg +0 -0
  45. data/docs/concepts.md +216 -0
  46. data/docs/examples/basic-chat.md +193 -0
  47. data/docs/examples/index.md +129 -0
  48. data/docs/examples/mcp-server.md +290 -0
  49. data/docs/examples/multi-robot-network.md +312 -0
  50. data/docs/examples/rails-application.md +420 -0
  51. data/docs/examples/tool-usage.md +310 -0
  52. data/docs/getting-started/configuration.md +230 -0
  53. data/docs/getting-started/index.md +56 -0
  54. data/docs/getting-started/installation.md +179 -0
  55. data/docs/getting-started/quick-start.md +203 -0
  56. data/docs/guides/building-robots.md +376 -0
  57. data/docs/guides/creating-networks.md +366 -0
  58. data/docs/guides/history.md +359 -0
  59. data/docs/guides/index.md +68 -0
  60. data/docs/guides/mcp-integration.md +356 -0
  61. data/docs/guides/memory.md +309 -0
  62. data/docs/guides/rails-integration.md +432 -0
  63. data/docs/guides/streaming.md +314 -0
  64. data/docs/guides/using-tools.md +394 -0
  65. data/docs/index.md +160 -0
  66. data/examples/01_simple_robot.rb +38 -0
  67. data/examples/02_tools.rb +106 -0
  68. data/examples/03_network.rb +103 -0
  69. data/examples/04_mcp.rb +219 -0
  70. data/examples/05_streaming.rb +124 -0
  71. data/examples/06_prompt_templates.rb +324 -0
  72. data/examples/07_network_memory.rb +329 -0
  73. data/examples/prompts/assistant/system.txt.erb +2 -0
  74. data/examples/prompts/assistant/user.txt.erb +1 -0
  75. data/examples/prompts/billing/system.txt.erb +7 -0
  76. data/examples/prompts/billing/user.txt.erb +1 -0
  77. data/examples/prompts/classifier/system.txt.erb +4 -0
  78. data/examples/prompts/classifier/user.txt.erb +1 -0
  79. data/examples/prompts/entity_extractor/system.txt.erb +11 -0
  80. data/examples/prompts/entity_extractor/user.txt.erb +3 -0
  81. data/examples/prompts/escalation/system.txt.erb +35 -0
  82. data/examples/prompts/escalation/user.txt.erb +34 -0
  83. data/examples/prompts/general/system.txt.erb +4 -0
  84. data/examples/prompts/general/user.txt.erb +1 -0
  85. data/examples/prompts/github_assistant/system.txt.erb +6 -0
  86. data/examples/prompts/github_assistant/user.txt.erb +1 -0
  87. data/examples/prompts/helper/system.txt.erb +1 -0
  88. data/examples/prompts/helper/user.txt.erb +1 -0
  89. data/examples/prompts/keyword_extractor/system.txt.erb +8 -0
  90. data/examples/prompts/keyword_extractor/user.txt.erb +3 -0
  91. data/examples/prompts/order_support/system.txt.erb +27 -0
  92. data/examples/prompts/order_support/user.txt.erb +22 -0
  93. data/examples/prompts/product_support/system.txt.erb +30 -0
  94. data/examples/prompts/product_support/user.txt.erb +32 -0
  95. data/examples/prompts/sentiment_analyzer/system.txt.erb +9 -0
  96. data/examples/prompts/sentiment_analyzer/user.txt.erb +3 -0
  97. data/examples/prompts/synthesizer/system.txt.erb +14 -0
  98. data/examples/prompts/synthesizer/user.txt.erb +15 -0
  99. data/examples/prompts/technical/system.txt.erb +7 -0
  100. data/examples/prompts/technical/user.txt.erb +1 -0
  101. data/examples/prompts/triage/system.txt.erb +16 -0
  102. data/examples/prompts/triage/user.txt.erb +17 -0
  103. data/lib/generators/robot_lab/install_generator.rb +78 -0
  104. data/lib/generators/robot_lab/robot_generator.rb +55 -0
  105. data/lib/generators/robot_lab/templates/initializer.rb.tt +41 -0
  106. data/lib/generators/robot_lab/templates/migration.rb.tt +32 -0
  107. data/lib/generators/robot_lab/templates/result_model.rb.tt +52 -0
  108. data/lib/generators/robot_lab/templates/robot.rb.tt +46 -0
  109. data/lib/generators/robot_lab/templates/robot_test.rb.tt +32 -0
  110. data/lib/generators/robot_lab/templates/routing_robot.rb.tt +53 -0
  111. data/lib/generators/robot_lab/templates/thread_model.rb.tt +40 -0
  112. data/lib/robot_lab/adapters/anthropic.rb +163 -0
  113. data/lib/robot_lab/adapters/base.rb +85 -0
  114. data/lib/robot_lab/adapters/gemini.rb +193 -0
  115. data/lib/robot_lab/adapters/openai.rb +159 -0
  116. data/lib/robot_lab/adapters/registry.rb +81 -0
  117. data/lib/robot_lab/configuration.rb +143 -0
  118. data/lib/robot_lab/error.rb +32 -0
  119. data/lib/robot_lab/errors.rb +70 -0
  120. data/lib/robot_lab/history/active_record_adapter.rb +146 -0
  121. data/lib/robot_lab/history/config.rb +115 -0
  122. data/lib/robot_lab/history/thread_manager.rb +93 -0
  123. data/lib/robot_lab/mcp/client.rb +210 -0
  124. data/lib/robot_lab/mcp/server.rb +84 -0
  125. data/lib/robot_lab/mcp/transports/base.rb +56 -0
  126. data/lib/robot_lab/mcp/transports/sse.rb +117 -0
  127. data/lib/robot_lab/mcp/transports/stdio.rb +133 -0
  128. data/lib/robot_lab/mcp/transports/streamable_http.rb +139 -0
  129. data/lib/robot_lab/mcp/transports/websocket.rb +108 -0
  130. data/lib/robot_lab/memory.rb +882 -0
  131. data/lib/robot_lab/memory_change.rb +123 -0
  132. data/lib/robot_lab/message.rb +357 -0
  133. data/lib/robot_lab/network.rb +350 -0
  134. data/lib/robot_lab/rails/engine.rb +29 -0
  135. data/lib/robot_lab/rails/railtie.rb +42 -0
  136. data/lib/robot_lab/robot.rb +560 -0
  137. data/lib/robot_lab/robot_result.rb +205 -0
  138. data/lib/robot_lab/robotic_model.rb +324 -0
  139. data/lib/robot_lab/state_proxy.rb +188 -0
  140. data/lib/robot_lab/streaming/context.rb +144 -0
  141. data/lib/robot_lab/streaming/events.rb +95 -0
  142. data/lib/robot_lab/streaming/sequence_counter.rb +48 -0
  143. data/lib/robot_lab/task.rb +117 -0
  144. data/lib/robot_lab/tool.rb +223 -0
  145. data/lib/robot_lab/tool_config.rb +112 -0
  146. data/lib/robot_lab/tool_manifest.rb +234 -0
  147. data/lib/robot_lab/user_message.rb +118 -0
  148. data/lib/robot_lab/version.rb +5 -0
  149. data/lib/robot_lab/waiter.rb +73 -0
  150. data/lib/robot_lab.rb +195 -0
  151. data/mkdocs.yml +214 -0
  152. data/sig/robot_lab.rbs +4 -0
  153. metadata +442 -0
@@ -0,0 +1,432 @@
1
+ # Rails Integration
2
+
3
+ RobotLab integrates seamlessly with Ruby on Rails applications.
4
+
5
+ ## Installation
6
+
7
+ ### Generate Files
8
+
9
+ ```bash
10
+ rails generate robot_lab:install
11
+ ```
12
+
13
+ This creates:
14
+
15
+ ```
16
+ config/initializers/robot_lab.rb # Configuration
17
+ db/migrate/*_create_robot_lab_tables.rb # Database tables
18
+ app/models/robot_lab_thread.rb # Thread model
19
+ app/models/robot_lab_result.rb # Result model
20
+ app/robots/ # Directory for robots
21
+ app/tools/ # Directory for tools
22
+ ```
23
+
24
+ ### Run Migrations
25
+
26
+ ```bash
27
+ rails db:migrate
28
+ ```
29
+
30
+ ## Configuration
31
+
32
+ ### Initializer
33
+
34
+ ```ruby title="config/initializers/robot_lab.rb"
35
+ RobotLab.configure do |config|
36
+ # API Keys from credentials
37
+ config.anthropic_api_key = Rails.application.credentials.anthropic_api_key
38
+ config.openai_api_key = Rails.application.credentials.openai_api_key
39
+
40
+ # Defaults
41
+ config.default_provider = :anthropic
42
+ config.default_model = "claude-sonnet-4"
43
+
44
+ # Rails logger
45
+ config.logger = Rails.logger
46
+
47
+ # Template path (auto-configured to app/prompts)
48
+ end
49
+ ```
50
+
51
+ ### Environment-Specific
52
+
53
+ ```ruby
54
+ RobotLab.configure do |config|
55
+ config.anthropic_api_key = Rails.application.credentials.anthropic_api_key
56
+
57
+ case Rails.env
58
+ when "development"
59
+ config.default_model = "claude-haiku-3" # Faster/cheaper
60
+ config.logger.level = :debug
61
+ when "test"
62
+ config.streaming_enabled = false
63
+ when "production"
64
+ config.default_model = "claude-sonnet-4"
65
+ end
66
+ end
67
+ ```
68
+
69
+ ### Application Config
70
+
71
+ ```ruby title="config/application.rb"
72
+ module MyApp
73
+ class Application < Rails::Application
74
+ config.robot_lab.default_model = "claude-sonnet-4"
75
+ config.robot_lab.default_provider = :anthropic
76
+ end
77
+ end
78
+ ```
79
+
80
+ ## Creating Robots
81
+
82
+ ### Robot Generator
83
+
84
+ ```bash
85
+ rails generate robot_lab:robot Support
86
+ rails generate robot_lab:robot Billing --description="Handles billing inquiries"
87
+ rails generate robot_lab:robot Router --routing
88
+ ```
89
+
90
+ ### Robot Class
91
+
92
+ ```ruby title="app/robots/support_robot.rb"
93
+ class SupportRobot
94
+ def self.build
95
+ RobotLab.build do
96
+ name "support"
97
+ description "Handles customer support inquiries"
98
+ model "claude-sonnet-4"
99
+
100
+ template "support/system_prompt"
101
+
102
+ tool :lookup_order do
103
+ description "Look up order by ID"
104
+ parameter :order_id, type: :string, required: true
105
+ handler { |order_id:, **_| Order.find_by(id: order_id)&.to_h }
106
+ end
107
+ end
108
+ end
109
+ end
110
+ ```
111
+
112
+ ### Using in Controllers
113
+
114
+ ```ruby title="app/controllers/chat_controller.rb"
115
+ class ChatController < ApplicationController
116
+ def create
117
+ network = build_network
118
+ state = RobotLab.create_state(
119
+ message: params[:message],
120
+ data: { user_id: current_user.id }
121
+ )
122
+
123
+ result = network.run(state: state)
124
+
125
+ render json: {
126
+ response: result.last_result.output.first.content,
127
+ thread_id: state.thread_id
128
+ }
129
+ end
130
+
131
+ private
132
+
133
+ def build_network
134
+ RobotLab.create_network do
135
+ name "customer_service"
136
+ add_robot SupportRobot.build
137
+ add_robot BillingRobot.build
138
+
139
+ history history_adapter.to_config
140
+ end
141
+ end
142
+
143
+ def history_adapter
144
+ RobotLab::History::ActiveRecordAdapter.new(
145
+ thread_model: RobotLabThread,
146
+ result_model: RobotLabResult
147
+ )
148
+ end
149
+ end
150
+ ```
151
+
152
+ ## Prompt Templates
153
+
154
+ ### Template Location
155
+
156
+ ```
157
+ app/prompts/
158
+ ├── support/
159
+ │ ├── system_prompt.erb
160
+ │ └── greeting.erb
161
+ └── billing/
162
+ └── system_prompt.erb
163
+ ```
164
+
165
+ ### Template Usage
166
+
167
+ ```ruby
168
+ robot = RobotLab.build do
169
+ name "support"
170
+ template "support/system_prompt", company: "Acme Corp"
171
+ end
172
+ ```
173
+
174
+ ```erb title="app/prompts/support/system_prompt.erb"
175
+ You are a support agent for <%= company %>.
176
+
177
+ Your responsibilities:
178
+ - Answer product questions
179
+ - Help with order issues
180
+ - Provide friendly assistance
181
+ ```
182
+
183
+ ## Action Cable Integration
184
+
185
+ ### Channel
186
+
187
+ ```ruby title="app/channels/chat_channel.rb"
188
+ class ChatChannel < ApplicationCable::Channel
189
+ def subscribed
190
+ stream_from "chat_#{params[:thread_id]}"
191
+ end
192
+
193
+ def receive(data)
194
+ message = data["message"]
195
+ thread_id = data["thread_id"]
196
+
197
+ state = RobotLab.create_state(message: message)
198
+ state.thread_id = thread_id if thread_id
199
+
200
+ network.run(
201
+ state: state,
202
+ streaming: ->(event) {
203
+ ActionCable.server.broadcast("chat_#{thread_id || state.thread_id}", event)
204
+ }
205
+ )
206
+ end
207
+
208
+ private
209
+
210
+ def network
211
+ @network ||= ChatNetwork.build
212
+ end
213
+ end
214
+ ```
215
+
216
+ ### JavaScript Client
217
+
218
+ ```javascript
219
+ const channel = consumer.subscriptions.create(
220
+ { channel: "ChatChannel", thread_id: threadId },
221
+ {
222
+ received(data) {
223
+ if (data.event === "delta") {
224
+ appendToMessage(data.data.content);
225
+ }
226
+ }
227
+ }
228
+ );
229
+
230
+ channel.send({ message: "Hello!", thread_id: threadId });
231
+ ```
232
+
233
+ ## Background Jobs
234
+
235
+ ### Async Processing
236
+
237
+ ```ruby title="app/jobs/process_message_job.rb"
238
+ class ProcessMessageJob < ApplicationJob
239
+ queue_as :default
240
+
241
+ def perform(thread_id:, message:, user_id:)
242
+ state = RobotLab.create_state(
243
+ message: message,
244
+ data: { user_id: user_id }
245
+ )
246
+ state.thread_id = thread_id
247
+
248
+ result = network.run(state: state)
249
+
250
+ # Notify user of completion
251
+ ActionCable.server.broadcast(
252
+ "chat_#{thread_id}",
253
+ { event: "complete", response: result.last_result.output.first.content }
254
+ )
255
+ end
256
+
257
+ private
258
+
259
+ def network
260
+ ChatNetwork.build
261
+ end
262
+ end
263
+ ```
264
+
265
+ ### Enqueue from Controller
266
+
267
+ ```ruby
268
+ ProcessMessageJob.perform_later(
269
+ thread_id: params[:thread_id],
270
+ message: params[:message],
271
+ user_id: current_user.id
272
+ )
273
+
274
+ render json: { status: "processing" }
275
+ ```
276
+
277
+ ## Testing
278
+
279
+ ### Test Configuration
280
+
281
+ ```ruby title="config/environments/test.rb"
282
+ Rails.application.configure do
283
+ config.robot_lab.streaming_enabled = false
284
+ end
285
+ ```
286
+
287
+ ### Robot Tests
288
+
289
+ ```ruby title="test/robots/support_robot_test.rb"
290
+ require "test_helper"
291
+
292
+ class SupportRobotTest < ActiveSupport::TestCase
293
+ test "builds valid robot" do
294
+ robot = SupportRobot.build
295
+ assert_equal "support", robot.name
296
+ assert_includes robot.tools.map(&:name), "lookup_order"
297
+ end
298
+ end
299
+ ```
300
+
301
+ ### Integration Tests
302
+
303
+ ```ruby title="test/integration/chat_test.rb"
304
+ require "test_helper"
305
+
306
+ class ChatTest < ActionDispatch::IntegrationTest
307
+ test "processes chat message" do
308
+ VCR.use_cassette("chat_response") do
309
+ post chat_path, params: { message: "Hello" }
310
+ assert_response :success
311
+
312
+ json = JSON.parse(response.body)
313
+ assert json["response"].present?
314
+ assert json["thread_id"].present?
315
+ end
316
+ end
317
+ end
318
+ ```
319
+
320
+ ## Models
321
+
322
+ ### Thread Model
323
+
324
+ ```ruby title="app/models/robot_lab_thread.rb"
325
+ class RobotLabThread < ApplicationRecord
326
+ has_many :results, class_name: "RobotLabResult", foreign_key: :thread_id
327
+ belongs_to :user, optional: true
328
+
329
+ scope :recent, -> { order(updated_at: :desc) }
330
+ scope :for_user, ->(user) { where(user: user) }
331
+ end
332
+ ```
333
+
334
+ ### Result Model
335
+
336
+ ```ruby title="app/models/robot_lab_result.rb"
337
+ class RobotLabResult < ApplicationRecord
338
+ belongs_to :thread, class_name: "RobotLabThread"
339
+
340
+ def to_robot_result
341
+ RobotLab::RobotResult.new(
342
+ robot_name: robot_name,
343
+ output: deserialize_messages(output_data),
344
+ tool_calls: deserialize_messages(tool_calls_data),
345
+ stop_reason: stop_reason
346
+ )
347
+ end
348
+
349
+ private
350
+
351
+ def deserialize_messages(data)
352
+ return [] unless data
353
+ data.map { |h| RobotLab::Message.from_hash(h.symbolize_keys) }
354
+ end
355
+ end
356
+ ```
357
+
358
+ ## Best Practices
359
+
360
+ ### 1. Use Service Objects
361
+
362
+ ```ruby title="app/services/chat_service.rb"
363
+ class ChatService
364
+ def initialize(user:)
365
+ @user = user
366
+ end
367
+
368
+ def process(message, thread_id: nil)
369
+ state = build_state(message, thread_id)
370
+ result = network.run(state: state)
371
+
372
+ {
373
+ response: result.last_result.output.first.content,
374
+ thread_id: state.thread_id
375
+ }
376
+ end
377
+
378
+ private
379
+
380
+ def build_state(message, thread_id)
381
+ state = RobotLab.create_state(
382
+ message: message,
383
+ data: { user_id: @user.id }
384
+ )
385
+ state.thread_id = thread_id if thread_id
386
+ state
387
+ end
388
+
389
+ def network
390
+ @network ||= ChatNetwork.build
391
+ end
392
+ end
393
+ ```
394
+
395
+ ### 2. Handle Errors
396
+
397
+ ```ruby
398
+ def create
399
+ result = ChatService.new(user: current_user).process(params[:message])
400
+ render json: result
401
+ rescue RobotLab::Error => e
402
+ render json: { error: e.message }, status: :unprocessable_entity
403
+ rescue StandardError => e
404
+ Rails.logger.error("Chat error: #{e.message}")
405
+ render json: { error: "An error occurred" }, status: :internal_server_error
406
+ end
407
+ ```
408
+
409
+ ### 3. Rate Limiting
410
+
411
+ ```ruby
412
+ class ChatController < ApplicationController
413
+ before_action :check_rate_limit
414
+
415
+ private
416
+
417
+ def check_rate_limit
418
+ key = "chat_rate:#{current_user.id}"
419
+ count = Rails.cache.increment(key, 1, expires_in: 1.minute)
420
+
421
+ if count > 10
422
+ render json: { error: "Rate limit exceeded" }, status: :too_many_requests
423
+ end
424
+ end
425
+ end
426
+ ```
427
+
428
+ ## Next Steps
429
+
430
+ - [Building Robots](building-robots.md) - Robot patterns
431
+ - [Creating Networks](creating-networks.md) - Network configuration
432
+ - [History Guide](history.md) - Conversation persistence